diff --git a/g10/getkey.c b/g10/getkey.c index f0e33c596..68e6a1bf0 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,4261 +1,4261 @@ /* getkey.c - Get a key from the database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2008, 2010 Free Software Foundation, Inc. * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "keydb.h" #include "options.h" #include "main.h" #include "trustdb.h" #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "host2net.h" #include "mbox-util.h" #include "status.h" #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE #if MAX_PK_CACHE_ENTRIES < 2 #error We need the cache for key creation #endif /* Flags values returned by the lookup code. Note that the values are * directly used by the KEY_CONSIDERED status line. */ #define LOOKUP_NOT_SELECTED (1<<0) #define LOOKUP_ALL_SUBKEYS_EXPIRED (1<<1) /* or revoked */ /* A context object used by the lookup functions. */ struct getkey_ctx_s { /* Part of the search criteria: whether the search is an exact search or not. A search that is exact requires that a key or subkey meet all of the specified criteria. A search that is not exact allows selecting a different key or subkey from the keyblock that matched the critera. Further, an exact search returns the key or subkey that matched whereas a non-exact search typically returns the primary key. See finish_lookup for details. */ int exact; /* Part of the search criteria: Whether the caller only wants keys with an available secret key. This is used by getkey_next to get the next result with the same initial criteria. */ int want_secret; /* Part of the search criteria: The type of the requested key. A mask of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If non-zero, then for a key to match, it must implement one of the required uses. */ int req_usage; /* The database handle. */ KEYDB_HANDLE kr_handle; /* Whether we should call xfree() on the context when the context is released using getkey_end()). */ int not_allocated; /* This variable is used as backing store for strings which have their address used in ITEMS. */ strlist_t extra_list; /* Part of the search criteria: The low-level search specification as passed to keydb_search. */ int nitems; /* This must be the last element in the structure. When we allocate the structure, we allocate it so that ITEMS can hold NITEMS. */ KEYDB_SEARCH_DESC items[1]; }; #if 0 static struct { int any; int okay_count; int nokey_count; int error_count; } lkup_stats[21]; #endif typedef struct keyid_list { struct keyid_list *next; char fpr[MAX_FINGERPRINT_LEN]; u32 keyid[2]; } *keyid_list_t; #if MAX_PK_CACHE_ENTRIES typedef struct pk_cache_entry { struct pk_cache_entry *next; u32 keyid[2]; PKT_public_key *pk; } *pk_cache_entry_t; static pk_cache_entry_t pk_cache; static int pk_cache_entries; /* Number of entries in pk cache. */ static int pk_cache_disabled; #endif #if MAX_UID_CACHE_ENTRIES < 5 #error we really need the userid cache #endif typedef struct user_id_db { struct user_id_db *next; keyid_list_t keyids; int len; char name[1]; } *user_id_db_t; static user_id_db_t user_id_db; static int uid_cache_entries; /* Number of entries in uid cache. */ static void merge_selfsigs (kbnode_t keyblock); static int lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, kbnode_t *ret_found_key, int want_secret); static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, unsigned int *r_flags); static void print_status_key_considered (kbnode_t keyblock, unsigned int flags); #if 0 static void print_stats () { int i; for (i = 0; i < DIM (lkup_stats); i++) { if (lkup_stats[i].any) es_fprintf (es_stderr, "lookup stats: mode=%-2d ok=%-6d nokey=%-6d err=%-6d\n", i, lkup_stats[i].okay_count, lkup_stats[i].nokey_count, lkup_stats[i].error_count); } } #endif /* Cache a copy of a public key in the public key cache. PK is not * cached if caching is disabled (via getkey_disable_caches), if * PK->FLAGS.DONT_CACHE is set, we don't know how to derive a key id * from the public key (e.g., unsupported algorithm), or a key with * the key id is already in the cache. * * The public key packet is copied into the cache using * copy_public_key. Thus, any secret parts are not copied, for * instance. * * This cache is filled by get_pubkey and is read by get_pubkey and * get_pubkey_fast. */ void cache_public_key (PKT_public_key * pk) { #if MAX_PK_CACHE_ENTRIES pk_cache_entry_t ce, ce2; u32 keyid[2]; if (pk_cache_disabled) return; if (pk->flags.dont_cache) return; if (is_ELGAMAL (pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH || is_RSA (pk->pubkey_algo)) { keyid_from_pk (pk, keyid); } else return; /* Don't know how to get the keyid. */ for (ce = pk_cache; ce; ce = ce->next) if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { if (DBG_CACHE) log_debug ("cache_public_key: already in cache\n"); return; } if (pk_cache_entries >= MAX_PK_CACHE_ENTRIES) { int n; /* Remove the last 50% of the entries. */ for (ce = pk_cache, n = 0; ce && n < pk_cache_entries/2; n++) ce = ce->next; if (ce && ce != pk_cache && ce->next) { ce2 = ce->next; ce->next = NULL; ce = ce2; for (; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); pk_cache_entries--; } } log_assert (pk_cache_entries < MAX_PK_CACHE_ENTRIES); } pk_cache_entries++; ce = xmalloc (sizeof *ce); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key (NULL, pk); ce->keyid[0] = keyid[0]; ce->keyid[1] = keyid[1]; #endif } /* Return a const utf-8 string with the text "[User ID not found]". This function is required so that we don't need to switch gettext's encoding temporary. */ static const char * user_id_not_found_utf8 (void) { static char *text; if (!text) text = native_to_utf8 (_("[User ID not found]")); return text; } /* Return the user ID from the given keyblock. * We use the primary uid flag which has been set by the merge_selfsigs * function. The returned value is only valid as long as the given * keyblock is not changed. */ static const char * get_primary_uid (KBNODE keyblock, size_t * uidlen) { KBNODE k; const char *s; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { *uidlen = k->pkt->pkt.user_id->len; return k->pkt->pkt.user_id->name; } } s = user_id_not_found_utf8 (); *uidlen = strlen (s); return s; } static void release_keyid_list (keyid_list_t k) { while (k) { keyid_list_t k2 = k->next; xfree (k); k = k2; } } /**************** * Store the association of keyid and userid * Feed only public keys to this function. */ static void cache_user_id (KBNODE keyblock) { user_id_db_t r; const char *uid; size_t uidlen; keyid_list_t keyids = NULL; KBNODE k; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { keyid_list_t a = xmalloc_clear (sizeof *a); /* Hmmm: For a long list of keyids it might be an advantage * to append the keys. */ fingerprint_from_pk (k->pkt->pkt.public_key, a->fpr, NULL); keyid_from_pk (k->pkt->pkt.public_key, a->keyid); /* First check for duplicates. */ for (r = user_id_db; r; r = r->next) { keyid_list_t b; for (b = r->keyids; b; b = b->next) { if (!memcmp (b->fpr, a->fpr, MAX_FINGERPRINT_LEN)) { if (DBG_CACHE) log_debug ("cache_user_id: already in cache\n"); release_keyid_list (keyids); xfree (a); return; } } } /* Now put it into the cache. */ a->next = keyids; keyids = a; } } if (!keyids) BUG (); /* No key no fun. */ uid = get_primary_uid (keyblock, &uidlen); if (uid_cache_entries >= MAX_UID_CACHE_ENTRIES) { /* fixme: use another algorithm to free some cache slots */ r = user_id_db; user_id_db = r->next; release_keyid_list (r->keyids); xfree (r); uid_cache_entries--; } r = xmalloc (sizeof *r + uidlen - 1); r->keyids = keyids; r->len = uidlen; memcpy (r->name, uid, r->len); r->next = user_id_db; user_id_db = r; uid_cache_entries++; } /* Disable and drop the public key cache (which is filled by cache_public_key and get_pubkey). Note: there is currently no way to reenable this cache. */ void getkey_disable_caches () { #if MAX_PK_CACHE_ENTRIES { pk_cache_entry_t ce, ce2; for (ce = pk_cache; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); } pk_cache_disabled = 1; pk_cache_entries = 0; pk_cache = NULL; } #endif /* fixme: disable user id cache ? */ } void pubkey_free (pubkey_t key) { if (key) { xfree (key->pk); release_kbnode (key->keyblock); xfree (key); } } void pubkeys_free (pubkey_t keys) { while (keys) { pubkey_t 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, pubkey_t *r_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; pubkey_t results = NULL; pubkey_t 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 (*r_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) { pubkey_t *prevp; pubkey_t next; pubkey_t 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) pubkeys_free (results); else *r_keys = results; return err; } static void pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key) { kbnode_t a = found_key ? found_key : keyblock; log_assert (a->pkt->pkttype == PKT_PUBLIC_KEY || a->pkt->pkttype == PKT_PUBLIC_SUBKEY); copy_public_key (pk, a->pkt->pkt.public_key); } /* Return the public key with the key id KEYID and store it at PK. * The resources in *PK should be released using * release_public_key_parts(). This function also stores a copy of * the public key in the user id cache (see cache_public_key). * * If PK is NULL, this function just stores the public key in the * cache and returns the usual return code. * * PK->REQ_USAGE (which is a mask of PUBKEY_USAGE_SIG, * PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT) is passed through to the * lookup function. If this is non-zero, only keys with the specified * usage will be returned. As such, it is essential that * PK->REQ_USAGE be correctly initialized! * * Returns 0 on success, GPG_ERR_NO_PUBKEY if there is no public key * with the specified key id, or another error code if an error * occurs. * * If the data was not read from the cache, then the self-signed data * has definitely been merged into the public key using * merge_selfsigs. */ int get_pubkey (PKT_public_key * pk, u32 * keyid) { int internal = 0; int rc = 0; #if MAX_PK_CACHE_ENTRIES if (pk) { /* Try to get it from the cache. We don't do this when pk is NULL as it does not guarantee that the user IDs are cached. */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) /* XXX: We don't check PK->REQ_USAGE here, but if we don't read from the cache, we do check it! */ { copy_public_key (pk, ce->pk); return 0; } } } #endif /* More init stuff. */ if (!pk) { pk = xmalloc_clear (sizeof *pk); internal++; } /* Do a lookup. */ { struct getkey_ctx_s ctx; KBNODE kb = NULL; KBNODE found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) { rc = gpg_error_from_syserror (); goto leave; } ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_usage = pk->req_usage; rc = lookup (&ctx, &kb, &found_key, 0); if (!rc) { pk_from_block (pk, kb, found_key); } getkey_end (&ctx); release_kbnode (kb); } if (!rc) goto leave; rc = GPG_ERR_NO_PUBKEY; leave: if (!rc) cache_public_key (pk); if (internal) free_public_key (pk); return rc; } /* Similar to get_pubkey, but it does not take PK->REQ_USAGE into * account nor does it merge in the self-signed data. This function * also only considers primary keys. It is intended to be used as a * quick check of the key to avoid recursion. It should only be used * in very certain cases. Like get_pubkey and unlike any of the other * lookup functions, this function also consults the user id cache * (see cache_public_key). * * Return the public key in *PK. The resources in *PK should be * released using release_public_key_parts(). */ int get_pubkey_fast (PKT_public_key * pk, u32 * keyid) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; u32 pkid[2]; log_assert (pk); #if MAX_PK_CACHE_ENTRIES { /* Try to get it from the cache */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] /* Only consider primary keys. */ && ce->pk->keyid[0] == ce->pk->main_keyid[0] && ce->pk->keyid[1] == ce->pk->main_keyid[1]) { if (pk) copy_public_key (pk, ce->pk); return 0; } } } #endif hd = keydb_new (); if (!hd) return gpg_error_from_syserror (); rc = keydb_search_kid (hd, keyid); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keydb_release (hd); return GPG_ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); return GPG_ERR_NO_PUBKEY; } log_assert (keyblock && keyblock->pkt && keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* We return the primary key. If KEYID matched a subkey, then we return an error. */ keyid_from_pk (keyblock->pkt->pkt.public_key, pkid); if (keyid[0] == pkid[0] && keyid[1] == pkid[1]) copy_public_key (pk, keyblock->pkt->pkt.public_key); else rc = GPG_ERR_NO_PUBKEY; release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return rc; } /* Return the key block for the key with key id KEYID or NULL, if an * error occurs. Use release_kbnode() to release the key block. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ kbnode_t get_pubkeyblock (u32 * keyid) { struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; memset (&ctx, 0, sizeof ctx); /* No need to set exact here because we want the entire block. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) return NULL; ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; rc = lookup (&ctx, &keyblock, NULL, 0); getkey_end (&ctx); return rc ? NULL : keyblock; } /* Return the public key with the key id KEYID iff the secret key is * available and store it at PK. The resources should be released * using release_public_key_parts(). * * Unlike other lookup functions, PK may not be NULL. PK->REQ_USAGE * is passed through to the lookup function and is a mask of * PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. Thus, it * must be valid! If this is non-zero, only keys with the specified * usage will be returned. * * Returns 0 on success. If a public key with the specified key id is * not found or a secret key is not available for that public key, an * error code is returned. Note: this function ignores legacy keys. * An error code is also return if an error occurs. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ gpg_error_t get_seckey (PKT_public_key *pk, u32 *keyid) { gpg_error_t err; struct getkey_ctx_s ctx; kbnode_t keyblock = NULL; kbnode_t found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) return gpg_error_from_syserror (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_usage = pk->req_usage; err = lookup (&ctx, &keyblock, &found_key, 1); if (!err) { pk_from_block (pk, keyblock, found_key); } getkey_end (&ctx); release_kbnode (keyblock); if (!err) { err = agent_probe_secret_key (/*ctrl*/NULL, pk); if (err) release_public_key_parts (pk); } return err; } /* Skip unusable keys. A key is unusable if it is revoked, expired or disabled or if the selected user id is revoked or expired. */ static int skip_unusable (void *dummy, u32 * keyid, int uid_no) { int unusable = 0; KBNODE keyblock; PKT_public_key *pk; (void) dummy; keyblock = get_pubkeyblock (keyid); if (!keyblock) { log_error ("error checking usability status of %s\n", keystr (keyid)); goto leave; } pk = keyblock->pkt->pkt.public_key; /* Is the key revoked or expired? */ if (pk->flags.revoked || pk->has_expired) unusable = 1; /* Is the user ID in question revoked or expired? */ if (!unusable && uid_no) { KBNODE node; int uids_seen = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *user_id = node->pkt->pkt.user_id; uids_seen ++; if (uids_seen != uid_no) continue; if (user_id->is_revoked || user_id->is_expired) unusable = 1; break; } } /* If UID_NO is non-zero, then the keyblock better have at least that many UIDs. */ log_assert (uids_seen == uid_no); } if (!unusable) unusable = pk_is_disabled (pk); leave: release_kbnode (keyblock); return unusable; } /* Search for keys matching some criteria. If RETCTX is not NULL, then the constructed context is returned in *RETCTX so that getpubkey_next can be used to get subsequent results. In this case, getkey_end() must be used to free the search context. If RETCTX is not NULL, then RET_KDBHD must be NULL. If NAMELIST is not NULL, then a search query is constructed using classify_user_id on each of the strings in the list. (Recall: the database does an OR of the terms, not an AND.) If NAMELIST is NULL, then all results are returned. If PK is not NULL, the public key of the first result is returned in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is set, it is used to filter the search results. See the documentation for finish_lookup to understand exactly how this is used. Note: The self-signed data has already been merged into the public key using merge_selfsigs. Free *PK by calling release_public_key_parts (or, if PK was allocated using xfree, you can use free_public_key, which calls release_public_key_parts(PK) and then xfree(PK)). If WANT_SECRET is set, then only keys with an available secret key (either locally or via key registered on a smartcard) are returned. If INCLUDE_UNUSABLE is set, then unusable keys (see the documentation for skip_unusable for an exact definition) are skipped unless they are looked up by key id or by fingerprint. If RET_KB is not NULL, the keyblock is returned in *RET_KB. This should be freed using release_kbnode(). If RET_KDBHD is not NULL, then the new database handle used to conduct the search is returned in *RET_KDBHD. This can be used to get subsequent results using keydb_search_next. Note: in this case, no advanced filtering is done for subsequent results (e.g., WANT_SECRET and PK->REQ_USAGE are not respected). This function returns 0 on success. Otherwise, an error code is returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY (if want_secret is set) is returned if the key is not found. */ static int key_byname (GETKEY_CTX *retctx, strlist_t namelist, PKT_public_key *pk, int want_secret, int include_unusable, KBNODE * ret_kb, KEYDB_HANDLE * ret_kdbhd) { int rc = 0; int n; strlist_t r; GETKEY_CTX ctx; KBNODE help_kb = NULL; KBNODE found_key = NULL; if (retctx) { /* Reset the returned context in case of error. */ log_assert (!ret_kdbhd); /* Not allowed because the handle is stored in the context. */ *retctx = NULL; } if (ret_kdbhd) *ret_kdbhd = NULL; if (!namelist) /* No search terms: iterate over the whole DB. */ { ctx = xmalloc_clear (sizeof *ctx); ctx->nitems = 1; ctx->items[0].mode = KEYDB_SEARCH_MODE_FIRST; if (!include_unusable) ctx->items[0].skipfnc = skip_unusable; } else { /* Build the search context. */ for (n = 0, r = namelist; r; r = r->next) n++; /* CTX has space for a single search term at the end. Thus, we need to allocate sizeof *CTX plus (n - 1) sizeof CTX->ITEMS. */ ctx = xmalloc_clear (sizeof *ctx + (n - 1) * sizeof ctx->items); ctx->nitems = n; for (n = 0, r = namelist; r; r = r->next, n++) { gpg_error_t err; err = classify_user_id (r->d, &ctx->items[n], 1); if (ctx->items[n].exact) ctx->exact = 1; if (err) { xfree (ctx); return gpg_err_code (err); /* FIXME: remove gpg_err_code. */ } if (!include_unusable && ctx->items[n].mode != KEYDB_SEARCH_MODE_SHORT_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_LONG_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR16 && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR20 && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR) ctx->items[n].skipfnc = skip_unusable; } } ctx->want_secret = want_secret; ctx->kr_handle = keydb_new (); if (!ctx->kr_handle) { rc = gpg_error_from_syserror (); getkey_end (ctx); return rc; } if (!ret_kb) ret_kb = &help_kb; if (pk) { ctx->req_usage = pk->req_usage; } rc = lookup (ctx, ret_kb, &found_key, want_secret); if (!rc && pk) { pk_from_block (pk, *ret_kb, found_key); } release_kbnode (help_kb); if (retctx) /* Caller wants the context. */ *retctx = ctx; else { if (ret_kdbhd) { *ret_kdbhd = ctx->kr_handle; ctx->kr_handle = NULL; } getkey_end (ctx); } return rc; } /* Find a public key identified by NAME. * * If name appears to be a valid valid RFC822 mailbox (i.e., email * address) and auto key lookup is enabled (no_akl == 0), then the * specified auto key lookup methods (--auto-key-lookup) are used to * import the key into the local keyring. Otherwise, just the local * keyring is consulted. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! PK->REQ_USAGE is * passed through to the lookup function and is a mask of * PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If this * is non-zero, only keys with the specified usage will be returned. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * NAME is a string, which is turned into a search query using * classify_user_id. * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * If RET_KDBHD is not NULL, then the new database handle used to * conduct the search is returned in *RET_KDBHD. This can be used to * get subsequent results using keydb_search_next or to modify the * returned record. Note: in this case, no advanced filtering is done * for subsequent results (e.g., PK->REQ_USAGE is not respected). * Unlike RETCTX, this is always returned. * * If INCLUDE_UNUSABLE is set, then unusable keys (see the * documentation for skip_unusable for an exact definition) are * skipped unless they are looked up by key id or by fingerprint. * * If NO_AKL is set, then the auto key locate functionality is * disabled and only the local key ring is considered. Note: the * local key ring is consulted even if local is not in the * --auto-key-locate option list! * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. */ int get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk, const char *name, KBNODE * ret_keyblock, KEYDB_HANDLE * ret_kdbhd, int include_unusable, int no_akl) { int rc; strlist_t namelist = NULL; struct akl *akl; int is_mbox; int nodefault = 0; int anylocalfirst = 0; /* If RETCTX is not NULL, then RET_KDBHD must be NULL. */ log_assert (retctx == NULL || ret_kdbhd == NULL); if (retctx) *retctx = NULL; /* Does NAME appear to be a mailbox (mail address)? */ is_mbox = is_valid_mailbox (name); /* The auto-key-locate feature works as follows: there are a number * of methods to look up keys. By default, the local keyring is * tried first. Then, each method listed in the --auto-key-locate is * tried in the order it appears. * * This can be changed as follows: * * - if nodefault appears anywhere in the list of options, then * the local keyring is not tried first, or, * * - if local appears anywhere in the list of options, then the * local keyring is not tried first, but in the order in which * it was listed in the --auto-key-locate option. * * Note: we only save the search context in RETCTX if the local * method is the first method tried (either explicitly or * implicitly). */ if (!no_akl) { /* auto-key-locate is enabled. */ /* nodefault is true if "nodefault" or "local" appear. */ for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type == AKL_NODEFAULT || akl->type == AKL_LOCAL) { nodefault = 1; break; } /* anylocalfirst is true if "local" appears before any other search methods (except "nodefault"). */ for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type != AKL_NODEFAULT) { if (akl->type == AKL_LOCAL) anylocalfirst = 1; break; } } if (!nodefault) { /* "nodefault" didn't occur. Thus, "local" is implicitly the * first method to try. */ anylocalfirst = 1; } if (nodefault && is_mbox) { /* Either "nodefault" or "local" (explicitly) appeared in the * auto key locate list and NAME appears to be an email address. * Don't try the local keyring. */ rc = GPG_ERR_NO_PUBKEY; } else { /* Either "nodefault" and "local" don't appear in the auto key * locate list (in which case we try the local keyring first) or * NAME does not appear to be an email address (in which case we * only try the local keyring). In this case, lookup NAME in * the local keyring. */ add_to_strlist (&namelist, name); rc = key_byname (retctx, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } /* If the requested name resembles a valid mailbox and automatic retrieval has been enabled, we try to import the key. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && !no_akl && is_mbox) { /* NAME wasn't present in the local keyring (or we didn't try * the local keyring). Since the auto key locate feature is * enabled and NAME appears to be an email address, try the auto * locate feature. */ for (akl = opt.auto_key_locate; akl; akl = akl->next) { unsigned char *fpr = NULL; size_t fpr_len; int did_akl_local = 0; int no_fingerprint = 0; const char *mechanism = "?"; switch (akl->type) { case AKL_NODEFAULT: /* This is a dummy mechanism. */ mechanism = "None"; rc = GPG_ERR_NO_PUBKEY; break; case AKL_LOCAL: mechanism = "Local"; did_akl_local = 1; if (retctx) { getkey_end (*retctx); *retctx = NULL; } add_to_strlist (&namelist, name); rc = key_byname (anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); break; case AKL_CERT: mechanism = "DNS CERT"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_cert (ctrl, name, 0, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_PKA: mechanism = "PKA"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_pka (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_DANE: mechanism = "DANE"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_cert (ctrl, name, 1, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_WKD: mechanism = "WKD"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_wkd (ctrl, name, 0, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_LDAP: mechanism = "LDAP"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_ldap (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_KEYSERVER: /* Strictly speaking, we don't need to only use a valid * mailbox for the getname search, but it helps cut down * on the problem of searching for something like "john" * and getting a whole lot of keys back. */ if (keyserver_any_configured (ctrl)) { mechanism = "keyserver"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, opt.keyserver); glo_ctrl.in_auto_key_retrieve--; } else { mechanism = "Unconfigured keyserver"; rc = GPG_ERR_NO_PUBKEY; } break; case AKL_SPEC: { struct keyserver_spec *keyserver; mechanism = akl->spec->uri; keyserver = keyserver_match (akl->spec); glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, keyserver); glo_ctrl.in_auto_key_retrieve--; } break; } /* Use the fingerprint of the key that we actually fetched. * This helps prevent problems where the key that we fetched * doesn't have the same name that we used to fetch it. In * the case of CERT and PKA, this is an actual security * requirement as the URL might point to a key put in by an * attacker. By forcing the use of the fingerprint, we * won't use the attacker's key here. */ if (!rc && fpr) { char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; log_assert (fpr_len <= MAX_FINGERPRINT_LEN); free_strlist (namelist); namelist = NULL; bin2hex (fpr, fpr_len, fpr_string); if (opt.verbose) log_info ("auto-key-locate found fingerprint %s\n", fpr_string); add_to_strlist (&namelist, fpr_string); } else if (!rc && !fpr && !did_akl_local) { /* The acquisition method said no failure occurred, but * it didn't return a fingerprint. That's a failure. */ no_fingerprint = 1; rc = GPG_ERR_NO_PUBKEY; } xfree (fpr); fpr = NULL; if (!rc && !did_akl_local) { /* There was no error and we didn't do a local lookup. * This means that we imported a key into the local * keyring. Try to read the imported key from the * keyring. */ if (retctx) { getkey_end (*retctx); *retctx = NULL; } rc = key_byname (anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } if (!rc) { /* Key found. */ log_info (_("automatically retrieved '%s' via %s\n"), name, mechanism); break; } if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY || opt.verbose || no_fingerprint) log_info (_("error retrieving '%s' via %s: %s\n"), name, mechanism, no_fingerprint ? _("No fingerprint") : gpg_strerror (rc)); } } if (rc && retctx) { getkey_end (*retctx); *retctx = NULL; } if (retctx && *retctx) { log_assert (!(*retctx)->extra_list); (*retctx)->extra_list = namelist; } else free_strlist (namelist); return rc; } /* Comparison machinery for get_best_pubkey_byname. */ /* First we have a struct to cache computed information about the key * in question. */ struct pubkey_cmp_cookie { int valid; /* Is this cookie valid? */ PKT_public_key key; /* The key. */ PKT_user_id *uid; /* The matching UID packet. */ unsigned int validity; /* Computed validity of (KEY, UID). */ u32 creation_time; /* Creation time of the newest subkey capable of encryption. */ }; /* Then we have a series of helper functions. */ static int key_is_ok (const PKT_public_key *key) { return (! key->has_expired && ! key->flags.revoked && key->flags.valid && ! key->flags.disabled); } static int uid_is_ok (const PKT_public_key *key, const PKT_user_id *uid) { return key_is_ok (key) && ! uid->is_revoked; } static int subkey_is_ok (const PKT_public_key *sub) { return ! sub->flags.revoked && sub->flags.valid && ! sub->flags.disabled; } /* Finally this function compares a NEW key to the former candidate * OLD. Returns < 0 if the old key is worse, > 0 if the old key is * better, == 0 if it is a tie. */ static int pubkey_cmp (ctrl_t ctrl, const char *name, struct pubkey_cmp_cookie *old, struct pubkey_cmp_cookie *new, KBNODE new_keyblock) { kbnode_t n; new->creation_time = 0; for (n = find_next_kbnode (new_keyblock, PKT_PUBLIC_SUBKEY); n; n = find_next_kbnode (n, PKT_PUBLIC_SUBKEY)) { PKT_public_key *sub = n->pkt->pkt.public_key; if ((sub->pubkey_usage & PUBKEY_USAGE_ENC) == 0) continue; if (! subkey_is_ok (sub)) continue; if (sub->timestamp > new->creation_time) new->creation_time = sub->timestamp; } for (n = find_next_kbnode (new_keyblock, PKT_USER_ID); n; n = find_next_kbnode (n, PKT_USER_ID)) { PKT_user_id *uid = n->pkt->pkt.user_id; char *mbox = mailbox_from_userid (uid->name); int match = mbox ? strcasecmp (name, mbox) == 0 : 0; xfree (mbox); if (! match) continue; new->uid = scopy_user_id (uid); new->validity = - get_validity (ctrl, &new->key, uid, NULL, 0) & TRUST_MASK; + get_validity (ctrl, new_keyblock, &new->key, uid, NULL, 0) & TRUST_MASK; new->valid = 1; if (! old->valid) return -1; /* No OLD key. */ if (! uid_is_ok (&old->key, old->uid) && uid_is_ok (&new->key, uid)) return -1; /* Validity of the NEW key is better. */ if (old->validity < new->validity) return -1; /* Validity of the NEW key is better. */ if (old->validity == new->validity && uid_is_ok (&new->key, uid) && old->creation_time < new->creation_time) return -1; /* Both keys are of the same validity, but the NEW key is newer. */ } /* Stick with the OLD key. */ return 1; } /* This function works like get_pubkey_byname, but if the name * resembles a mail address, the results are ranked and only the best * result is returned. */ int get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, int include_unusable, int no_akl) { int rc; struct getkey_ctx_s *ctx = NULL; if (retctx) *retctx = NULL; rc = get_pubkey_byname (ctrl, &ctx, pk, name, ret_keyblock, NULL, include_unusable, no_akl); if (rc) { if (ctx) getkey_end (ctx); return rc; } if (is_valid_mailbox (name) && ctx) { /* Rank results and return only the most relevant key. */ struct pubkey_cmp_cookie best = { 0 }, new; KBNODE new_keyblock; while (getkey_next (ctx, &new.key, &new_keyblock) == 0) { int diff = pubkey_cmp (ctrl, name, &best, &new, new_keyblock); release_kbnode (new_keyblock); if (diff < 0) { /* New key is better. */ release_public_key_parts (&best.key); free_user_id (best.uid); best = new; } else if (diff > 0) { /* Old key is better. */ release_public_key_parts (&new.key); free_user_id (new.uid); } else { /* A tie. Keep the old key. */ release_public_key_parts (&new.key); free_user_id (new.uid); } } getkey_end (ctx); ctx = NULL; free_user_id (best.uid); if (best.valid) { if (retctx || ret_keyblock) { ctx = xtrycalloc (1, sizeof **retctx); if (! ctx) rc = gpg_error_from_syserror (); else { ctx->kr_handle = keydb_new (); if (! ctx->kr_handle) { xfree (ctx); *retctx = NULL; rc = gpg_error_from_syserror (); } else { u32 *keyid = pk_keyid (&best.key); ctx->exact = 1; ctx->nitems = 1; ctx->items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx->items[0].u.kid[0] = keyid[0]; ctx->items[0].u.kid[1] = keyid[1]; if (ret_keyblock) { release_kbnode (*ret_keyblock); *ret_keyblock = NULL; rc = getkey_next (ctx, NULL, ret_keyblock); } } } } if (pk) *pk = best.key; else release_public_key_parts (&best.key); } } if (rc && ctx) { getkey_end (ctx); ctx = NULL; } if (retctx && ctx) *retctx = ctx; else getkey_end (ctx); return rc; } /* Get a public key from a file. * * PK is the buffer to store the key. The caller needs to make sure * that PK->REQ_USAGE is valid. PK->REQ_USAGE is passed through to * the lookup function and is a mask of PUBKEY_USAGE_SIG, * PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If this is non-zero, only * keys with the specified usage will be returned. * * FNAME is the file name. That file should contain exactly one * keyblock. * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY is returned if the key * is not found. * * The self-signed data has already been merged into the public key * using merge_selfsigs. The caller must release the content of PK by * calling release_public_key_parts (or, if PK was malloced, using * free_public_key). */ gpg_error_t get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) { gpg_error_t err; kbnode_t keyblock; kbnode_t found_key; unsigned int infoflags; err = read_key_from_file (ctrl, fname, &keyblock); if (!err) { /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. FIXME: Check whether this still holds. */ merge_selfsigs (keyblock); found_key = finish_lookup (keyblock, pk->req_usage, 0, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) pk_from_block (pk, keyblock, found_key); else err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } release_kbnode (keyblock); return err; } /* Lookup a key with the specified fingerprint. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: this function does an exact search and thus the * returned public key may be a subkey rather than the primary key. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If PK->REQ_USAGE is set, it is used to filter the search results. * (Thus, if PK is not NULL, PK->REQ_USAGE must be valid!!!) See the * documentation for finish_lookup to understand exactly how this is * used. * * If R_KEYBLOCK is not NULL, then the first result's keyblock is * returned in *R_KEYBLOCK. This should be freed using * release_kbnode(). * * FPRINT is a byte array whose contents is the fingerprint to use as * the search term. FPRINT_LEN specifies the length of the * fingerprint (in bytes). Currently, only 16 and 20-byte * fingerprints are supported. * * FIXME: We should replace this with the _byname function. This can * be done by creating a userID conforming to the unified fingerprint * style. */ int get_pubkey_byfprint (PKT_public_key *pk, kbnode_t *r_keyblock, const byte * fprint, size_t fprint_len) { int rc; if (r_keyblock) *r_keyblock = NULL; if (fprint_len == 20 || fprint_len == 16) { struct getkey_ctx_s ctx; KBNODE kb = NULL; KBNODE found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) return gpg_error_from_syserror (); ctx.nitems = 1; ctx.items[0].mode = fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy (ctx.items[0].u.fpr, fprint, fprint_len); rc = lookup (&ctx, &kb, &found_key, 0); if (!rc && pk) pk_from_block (pk, kb, found_key); if (!rc && r_keyblock) { *r_keyblock = kb; kb = NULL; } release_kbnode (kb); getkey_end (&ctx); } else rc = GPG_ERR_GENERAL; /* Oops */ return rc; } /* This function is similar to get_pubkey_byfprint, but it doesn't * merge the self-signed data into the public key and subkeys or into * the user ids. It also doesn't add the key to the user id cache. * Further, this function ignores PK->REQ_USAGE. * * This function is intended to avoid recursion and, as such, should * only be used in very specific situations. * * Like get_pubkey_byfprint, PK may be NULL. In that case, this * function effectively just checks for the existence of the key. */ int get_pubkey_byfprint_fast (PKT_public_key * pk, const byte * fprint, size_t fprint_len) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; byte fprbuf[MAX_FINGERPRINT_LEN]; int i; for (i = 0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++) fprbuf[i] = fprint[i]; while (i < MAX_FINGERPRINT_LEN) fprbuf[i++] = 0; hd = keydb_new (); if (!hd) return gpg_error_from_syserror (); rc = keydb_search_fpr (hd, fprbuf); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keydb_release (hd); return GPG_ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); return GPG_ERR_NO_PUBKEY; } log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (pk) copy_public_key (pk, keyblock->pkt->pkt.public_key); release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return 0; } const char * parse_def_secret_key (ctrl_t ctrl) { KEYDB_HANDLE hd = NULL; strlist_t t; static int warned; for (t = opt.def_secret_key; t; t = t->next) { gpg_error_t err; KEYDB_SEARCH_DESC desc; KBNODE kb; KBNODE node; err = classify_user_id (t->d, &desc, 1); if (err) { log_error (_("secret key \"%s\" not found: %s\n"), t->d, gpg_strerror (err)); if (!opt.quiet) log_info (_("(check argument of option '%s')\n"), "--default-key"); continue; } if (! hd) { hd = keydb_new (); if (!hd) return NULL; } else keydb_search_reset (hd); err = keydb_search (hd, &desc, 1, NULL); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) continue; if (err) { log_error (_("key \"%s\" not found: %s\n"), t->d, gpg_strerror (err)); t = NULL; break; } err = keydb_get_keyblock (hd, &kb); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); continue; } merge_selfsigs (kb); err = gpg_error (GPG_ERR_NO_SECKEY); node = kb; do { PKT_public_key *pk = node->pkt->pkt.public_key; /* Check that the key has the signing capability. */ if (! (pk->pubkey_usage & PUBKEY_USAGE_SIG)) continue; /* Check if the key is valid. */ if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "revoked"); continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "expired"); continue; } if (pk_is_disabled (pk)) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "disabled"); continue; } err = agent_probe_secret_key (ctrl, pk); if (! err) /* This is a valid key. */ break; } while ((node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY))); release_kbnode (kb); if (err) { if (! warned && ! opt.quiet) { log_info (_("Warning: not using '%s' as default key: %s\n"), t->d, gpg_strerror (GPG_ERR_NO_SECKEY)); print_reported_error (err, GPG_ERR_NO_SECKEY); } } else { if (! warned && ! opt.quiet) log_info (_("using \"%s\" as default secret key for signing\n"), t->d); break; } } if (! warned && opt.def_secret_key && ! t) log_info (_("all values passed to '%s' ignored\n"), "--default-key"); warned = 1; if (hd) keydb_release (hd); if (t) return t->d; return NULL; } /* Look up a secret key. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If --default-key was set, then the specified key is looked up. (In * this case, the default key is returned even if it is considered * unusable. See the documentation for skip_unusable for exactly what * this means.) * * Otherwise, this initiates a DB scan that returns all keys that are * usable (see previous paragraph for exactly what usable means) and * for which a secret key is available. * * This function returns the first match. Additional results can be * returned using getkey_next. */ gpg_error_t get_seckey_default (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; strlist_t namelist = NULL; int include_unusable = 1; const char *def_secret_key = parse_def_secret_key (ctrl); if (def_secret_key) add_to_strlist (&namelist, def_secret_key); else include_unusable = 0; err = key_byname (NULL, namelist, pk, 1, include_unusable, NULL, NULL); free_strlist (namelist); return err; } /* Search for keys matching some criteria. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If NAMES is not NULL, then a search query is constructed using * classify_user_id on each of the strings in the list. (Recall: the * database does an OR of the terms, not an AND.) If NAMES is * NULL, then all results are returned. * * If WANT_SECRET is set, then only keys with an available secret key * (either locally or via key registered on a smartcard) are returned. * * This function does not skip unusable keys (see the documentation * for skip_unusable for an exact definition). * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. */ gpg_error_t getkey_bynames (getkey_ctx_t *retctx, PKT_public_key *pk, strlist_t names, int want_secret, kbnode_t *ret_keyblock) { return key_byname (retctx, names, pk, want_secret, 1, ret_keyblock, NULL); } /* Search for one key matching some criteria. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If NAME is not NULL, then a search query is constructed using * classify_user_id on the string. In this case, even unusable keys * (see the documentation for skip_unusable for an exact definition of * unusable) are returned. Otherwise, if --default-key was set, then * that key is returned (even if it is unusable). If neither of these * conditions holds, then the first usable key is returned. * * If WANT_SECRET is set, then only keys with an available secret key * (either locally or via key registered on a smartcard) are returned. * * This function does not skip unusable keys (see the documentation * for skip_unusable for an exact definition). * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. * * FIXME: We also have the get_pubkey_byname function which has a * different semantic. Should be merged with this one. */ gpg_error_t getkey_byname (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk, const char *name, int want_secret, kbnode_t *ret_keyblock) { gpg_error_t err; strlist_t namelist = NULL; int with_unusable = 1; const char *def_secret_key = NULL; if (want_secret && !name) def_secret_key = parse_def_secret_key (ctrl); if (want_secret && !name && def_secret_key) add_to_strlist (&namelist, def_secret_key); else if (name) add_to_strlist (&namelist, name); else with_unusable = 0; err = key_byname (retctx, namelist, pk, want_secret, with_unusable, ret_keyblock, NULL); /* FIXME: Check that we really return GPG_ERR_NO_SECKEY if WANT_SECRET has been used. */ free_strlist (namelist); return err; } /* Return the next search result. * * If PK is not NULL, the public key of the next result is returned in * *PK. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xmalloc, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * RET_KEYBLOCK can be given as NULL; if it is not NULL it the entire * found keyblock is returned which must be released with * release_kbnode. If the function returns an error NULL is stored at * RET_KEYBLOCK. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ gpg_error_t getkey_next (getkey_ctx_t ctx, PKT_public_key *pk, kbnode_t *ret_keyblock) { int rc; /* Fixme: Make sure this is proper gpg_error */ KBNODE keyblock = NULL; KBNODE found_key = NULL; /* We need to disable the caching so that for an exact key search we won't get the result back from the cache and thus end up in an endless loop. The endless loop can occur, because the cache is used without respecting the current file pointer! */ keydb_disable_caching (ctx->kr_handle); /* FOUND_KEY is only valid as long as RET_KEYBLOCK is. If the * caller wants PK, but not RET_KEYBLOCK, we need hand in our own * keyblock. */ if (pk && ret_keyblock == NULL) ret_keyblock = &keyblock; rc = lookup (ctx, ret_keyblock, &found_key, ctx->want_secret); if (!rc && pk) { log_assert (found_key); pk_from_block (pk, NULL, found_key); release_kbnode (keyblock); } return rc; } /* Release any resources used by a key listing context. This must be * called on the context returned by, e.g., getkey_byname. */ void getkey_end (getkey_ctx_t ctx) { if (ctx) { keydb_release (ctx->kr_handle); free_strlist (ctx->extra_list); if (!ctx->not_allocated) xfree (ctx); } } /************************************************ ************* Merging stuff ******************** ************************************************/ /* Set the mainkey_id fields for all keys in KEYBLOCK. This is * usually done by merge_selfsigs but at some places we only need the * main_kid not a full merge. The function also guarantees that all * pk->keyids are computed. */ void setup_main_keyids (kbnode_t keyblock) { u32 kid[2], mainkid[2]; kbnode_t kbctx, node; PKT_public_key *pk; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) BUG (); pk = keyblock->pkt->pkt.public_key; keyid_from_pk (pk, mainkid); for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (!(node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, kid); /* Make sure pk->keyid is set. */ if (!pk->main_keyid[0] && !pk->main_keyid[1]) { pk->main_keyid[0] = mainkid[0]; pk->main_keyid[1] = mainkid[1]; } } } /* KEYBLOCK corresponds to a public key block. This function merges * much of the information from the self-signed data into the public * key, public subkey and user id data structures. If you use the * high-level search API (e.g., get_pubkey) for looking up key blocks, * then you don't need to call this function. This function is * useful, however, if you change the keyblock, e.g., by adding or * removing a self-signed data packet. */ void merge_keys_and_selfsig (KBNODE keyblock) { if (!keyblock) ; else if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) merge_selfsigs (keyblock); else log_debug ("FIXME: merging secret key blocks is not anymore available\n"); } static int parse_key_usage (PKT_signature * sig) { int key_usage = 0; const byte *p; size_t n; byte flags; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n); if (p && n) { /* First octet of the keyflags. */ flags = *p; if (flags & 1) { key_usage |= PUBKEY_USAGE_CERT; flags &= ~1; } if (flags & 2) { key_usage |= PUBKEY_USAGE_SIG; flags &= ~2; } /* We do not distinguish between encrypting communications and encrypting storage. */ if (flags & (0x04 | 0x08)) { key_usage |= PUBKEY_USAGE_ENC; flags &= ~(0x04 | 0x08); } if (flags & 0x20) { key_usage |= PUBKEY_USAGE_AUTH; flags &= ~0x20; } if (flags) key_usage |= PUBKEY_USAGE_UNKNOWN; if (!key_usage) key_usage |= PUBKEY_USAGE_NONE; } else if (p) /* Key flags of length zero. */ key_usage |= PUBKEY_USAGE_NONE; /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a capability that we do not handle. This serves to distinguish between a zero key usage which we handle as the default capabilities for that algorithm, and a usage that we do not handle. Likewise we use PUBKEY_USAGE_NONE to indicate that key_flags have been given but they do not specify any usage. */ return key_usage; } /* Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: * - weather the UID has been revoked * - assumed creation date of the UID * - temporary store the keyflags here * - temporary store the key expiration time here * - mark whether the primary user ID flag hat been set. * - store the preferences */ static void fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; const byte *p, *sym, *hash, *zip; size_t n, nsym, nhash, nzip; sig->flags.chosen_selfsig = 1;/* We chose this one. */ uid->created = 0; /* Not created == invalid. */ if (IS_UID_REV (sig)) { uid->is_revoked = 1; return; /* Has been revoked. */ } else uid->is_revoked = 0; uid->expiredate = sig->expiredate; if (sig->flags.expired) { uid->is_expired = 1; return; /* Has expired. */ } else uid->is_expired = 0; uid->created = sig->timestamp; /* This one is okay. */ uid->selfsigversion = sig->version; /* If we got this far, it's not expired :) */ uid->is_expired = 0; /* Store the key flags in the helper variable for later processing. */ uid->help_key_usage = parse_key_usage (sig); /* Ditto for the key expiration. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) uid->help_key_expire = keycreated + buf32_to_u32 (p); else uid->help_key_expire = 0; /* Set the primary user ID flag - we will later wipe out some * of them to only have one in our keyblock. */ uid->is_primary = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) uid->is_primary = 2; /* We could also query this from the unhashed area if it is not in * the hased area and then later try to decide which is the better * there should be no security problem with this. * For now we only look at the hashed one. */ /* Now build the preferences list. These must come from the hashed section so nobody can modify the ciphers a key is willing to accept. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM, &n); sym = p; nsym = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH, &n); hash = p; nhash = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR, &n); zip = p; nzip = p ? n : 0; if (uid->prefs) xfree (uid->prefs); n = nsym + nhash + nzip; if (!n) uid->prefs = NULL; else { uid->prefs = xmalloc (sizeof (*uid->prefs) * (n + 1)); n = 0; for (; nsym; nsym--, n++) { uid->prefs[n].type = PREFTYPE_SYM; uid->prefs[n].value = *sym++; } for (; nhash; nhash--, n++) { uid->prefs[n].type = PREFTYPE_HASH; uid->prefs[n].value = *hash++; } for (; nzip; nzip--, n++) { uid->prefs[n].type = PREFTYPE_ZIP; uid->prefs[n].value = *zip++; } uid->prefs[n].type = PREFTYPE_NONE; /* End of list marker */ uid->prefs[n].value = 0; } /* See whether we have the MDC feature. */ uid->flags.mdc = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x01)) uid->flags.mdc = 1; /* And the keyserver modify flag. */ uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); if (p && n && (p[0] & 0x80)) uid->flags.ks_modify = 0; } static void sig_to_revoke_info (PKT_signature * sig, struct revoke_info *rinfo) { rinfo->date = sig->timestamp; rinfo->algo = sig->pubkey_algo; rinfo->keyid[0] = sig->keyid[0]; rinfo->keyid[1] = sig->keyid[1]; } /* Given a keyblock, parse the key block and extract various pieces of information and save them with the primary key packet and the user id packets. For instance, some information is stored in signature packets. We find the latest such valid packet (since the user can change that information) and copy its contents into the PKT_public_key. Note that R_REVOKED may be set to 0, 1 or 2. This function fills in the following fields in the primary key's keyblock: main_keyid (computed) revkey / numrevkeys (derived from self signed key data) flags.valid (whether we have at least 1 self-sig) flags.maybe_revoked (whether a designed revoked the key, but we are missing the key to check the sig) selfsigversion (highest version of any valid self-sig) pubkey_usage (derived from most recent self-sig or most recent user id) has_expired (various sources) expiredate (various sources) See the documentation for fixup_uidnode for how the user id packets are modified. In addition to that the primary user id's is_primary field is set to 1 and the other user id's is_primary are set to 0. */ static void merge_selfsigs_main (KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo) { PKT_public_key *pk = NULL; KBNODE k; u32 kid[2]; u32 sigdate, uiddate, uiddate2; KBNODE signode, uidnode, uidnode2; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; int key_expire_seen = 0; byte sigversion = 0; *r_revoked = 0; memset (rinfo, 0, sizeof (*rinfo)); /* Section 11.1 of RFC 4880 determines the order of packets within a message. There are three sections, which must occur in the following order: the public key, the user ids and user attributes and the subkeys. Within each section, each primary packet (e.g., a user id packet) is followed by one or more signature packets, which modify that packet. */ /* According to Section 11.1 of RFC 4880, the public key must be the first packet. */ if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) /* parse_keyblock_image ensures that the first packet is the public key. */ BUG (); pk = keyblock->pkt->pkt.public_key; keytimestamp = pk->timestamp; keyid_from_pk (pk, kid); pk->main_keyid[0] = kid[0]; pk->main_keyid[1] = kid[1]; if (pk->version < 4) { /* Before v4 the key packet itself contains the expiration date * and there was no way to change it, so we start with the one * from the key packet. */ key_expire = pk->max_expiredate; key_expire_seen = 1; } /* First pass: - Find the latest direct key self-signature. We assume that the newest one overrides all others. - Determine whether the key has been revoked. - Gather all revocation keys (unlike other data, we don't just take them from the latest self-signed packet). - Determine max (sig[...]->version). */ /* Reset this in case this key was already merged. */ xfree (pk->revkey); pk->revkey = NULL; pk->numrevkeys = 0; signode = NULL; sigdate = 0; /* Helper variable to find the latest signature. */ /* According to Section 11.1 of RFC 4880, the public key comes first and is immediately followed by any signature packets that modify it. */ for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID && k->pkt->pkttype != PKT_ATTRIBUTE && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) /* Self sig. */ { if (check_key_signature (keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_KEY_REV (sig)) { /* Key has been revoked - there is no way to * override such a revocation, so we theoretically * can stop now. We should not cope with expiration * times for revocations here because we have to * assume that an attacker can generate all kinds of * signatures. However due to the fact that the key * has been revoked it does not harm either and by * continuing we gather some more info on that * key. */ *r_revoked = 1; sig_to_revoke_info (sig, rinfo); } else if (IS_KEY_SIG (sig)) { /* Add the indicated revocations keys from all signatures not just the latest. We do this because you need multiple 1F sigs to properly handle revocation keys (PGP does it this way, and a revocation key could be sensitive and hence in a different signature). */ if (sig->revkey) { int i; pk->revkey = xrealloc (pk->revkey, sizeof (struct revocation_key) * (pk->numrevkeys + sig->numrevkeys)); for (i = 0; i < sig->numrevkeys; i++) memcpy (&pk->revkey[pk->numrevkeys++], &sig->revkey[i], sizeof (struct revocation_key)); } if (sig->timestamp >= sigdate) /* This is the latest signature so far. */ { if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; if (sig->version > sigversion) sigversion = sig->version; } } } } } } /* Remove dupes from the revocation keys. */ if (pk->revkey) { int i, j, x, changed = 0; for (i = 0; i < pk->numrevkeys; i++) { for (j = i + 1; j < pk->numrevkeys; j++) { if (memcmp (&pk->revkey[i], &pk->revkey[j], sizeof (struct revocation_key)) == 0) { /* remove j */ for (x = j; x < pk->numrevkeys - 1; x++) pk->revkey[x] = pk->revkey[x + 1]; pk->numrevkeys--; j--; changed = 1; } } } if (changed) pk->revkey = xrealloc (pk->revkey, pk->numrevkeys * sizeof (struct revocation_key)); } if (signode) /* SIGNODE is the 1F signature packet with the latest creation time. Extract some information from it. */ { /* Some information from a direct key signature take precedence * over the same information given in UID sigs. */ PKT_signature *sig = signode->pkt->pkt.signature; const byte *p; key_usage = parse_key_usage (sig); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) { key_expire = keytimestamp + buf32_to_u32 (p); key_expire_seen = 1; } /* Mark that key as valid: One direct key signature should * render a key as valid. */ pk->flags.valid = 1; } /* Pass 1.5: Look for key revocation signatures that were not made by the key (i.e. did a revocation key issue a revocation for us?). Only bother to do this if there is a revocation key in the first place and we're not revoked already. */ if (!*r_revoked && pk->revkey) for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (IS_KEY_REV (sig) && (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1])) { int rc = check_revocation_keys (pk, sig); if (rc == 0) { *r_revoked = 2; sig_to_revoke_info (sig, rinfo); /* Don't continue checking since we can't be any more revoked than this. */ break; } else if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) pk->flags.maybe_revoked = 1; /* A failure here means the sig did not verify, was not issued by a revocation key, or a revocation key loop was broken. If a revocation key isn't findable, however, the key might be revoked and we don't know it. */ /* TODO: In the future handle subkey and cert revocations? PGP doesn't, but it's in 2440. */ } } } /* Second pass: Look at the self-signature of all user IDs. */ /* According to RFC 4880 section 11.1, user id and attribute packets are in the second section, after the public key packet and before the subkey packets. */ signode = uidnode = NULL; sigdate = 0; /* Helper variable to find the latest signature in one UID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID || k->pkt->pkttype == PKT_ATTRIBUTE) /* New user id packet. */ { if (uidnode && signode) /* Apply the data from the most recent self-signed packet to the preceding user id packet. */ { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } /* Clear SIGNODE. The only relevant self-signed data for UIDNODE follows it. */ if (k->pkt->pkttype == PKT_USER_ID) uidnode = k; else uidnode = NULL; signode = NULL; sigdate = 0; } else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) { if (check_key_signature (keyblock, k, NULL)) ; /* signature did not verify */ else if ((IS_UID_SIG (sig) || IS_UID_REV (sig)) && sig->timestamp >= sigdate) { /* Note: we allow invalidation of cert revocations * by a newer signature. An attacker can't use this * because a key should be revoked with a key revocation. * The reason why we have to allow for that is that at * one time an email address may become invalid but later * the same email address may become valid again (hired, * fired, hired again). */ sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; if (sig->version > sigversion) sigversion = sig->version; } } } } if (uidnode && signode) { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } /* If the key isn't valid yet, and we have --allow-non-selfsigned-uid set, then force it valid. */ if (!pk->flags.valid && opt.allow_non_selfsigned_uid) { if (opt.verbose) log_info (_("Invalid key %s made valid by" " --allow-non-selfsigned-uid\n"), keystr_from_pk (pk)); pk->flags.valid = 1; } /* The key STILL isn't valid, so try and find an ultimately trusted signature. */ if (!pk->flags.valid) { uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) uidnode = k; else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1]) { PKT_public_key *ultimate_pk; ultimate_pk = xmalloc_clear (sizeof (*ultimate_pk)); /* We don't want to use the full get_pubkey to avoid infinite recursion in certain cases. There is no reason to check that an ultimately trusted key is still valid - if it has been revoked the user should also remove the ultimate trust flag. */ if (get_pubkey_fast (ultimate_pk, sig->keyid) == 0 && check_key_signature2 (keyblock, k, ultimate_pk, NULL, NULL, NULL, NULL) == 0 && get_ownertrust (ultimate_pk) == TRUST_ULTIMATE) { free_public_key (ultimate_pk); pk->flags.valid = 1; break; } free_public_key (ultimate_pk); } } } } /* Record the highest selfsig version so we know if this is a v3 key through and through, or a v3 key with a v4 selfsig somewhere. This is useful in a few places to know if the key must be treated as PGP2-style or OpenPGP-style. Note that a selfsig revocation with a higher version number will also raise this value. This is okay since such a revocation must be issued by the user (i.e. it cannot be issued by someone else to modify the key behavior.) */ pk->selfsigversion = sigversion; /* Now that we had a look at all user IDs we can now get some information * from those user IDs. */ if (!key_usage) { /* Find the latest user ID with key flags set. */ uiddate = 0; /* Helper to find the latest user ID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_usage && uid->created > uiddate) { key_usage = uid->help_key_usage; uiddate = uid->created; } } } } if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (pk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (pk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } /* Whatever happens, it's a primary key, so it can certify. */ pk->pubkey_usage = key_usage | PUBKEY_USAGE_CERT; if (!key_expire_seen) { /* Find the latest valid user ID with a key expiration set * Note, that this may be a different one from the above because * some user IDs may have no expiration date set. */ uiddate = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_expire && uid->created > uiddate) { key_expire = uid->help_key_expire; uiddate = uid->created; } } } } /* Currently only v3 keys have a maximum expiration date, but I'll bet v5 keys get this feature again. */ if (key_expire == 0 || (pk->max_expiredate && key_expire > pk->max_expiredate)) key_expire = pk->max_expiredate; pk->has_expired = key_expire >= curtime ? 0 : key_expire; pk->expiredate = key_expire; /* Fixme: we should see how to get rid of the expiretime fields but * this needs changes at other places too. */ /* And now find the real primary user ID and delete all others. */ uiddate = uiddate2 = 0; uidnode = uidnode2 = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->is_primary) { if (uid->created > uiddate) { uiddate = uid->created; uidnode = k; } else if (uid->created == uiddate && uidnode) { /* The dates are equal, so we need to do a different (and arbitrary) comparison. This should rarely, if ever, happen. It's good to try and guarantee that two different GnuPG users with two different keyrings at least pick the same primary. */ if (cmp_user_ids (uid, uidnode->pkt->pkt.user_id) > 0) uidnode = k; } } else { if (uid->created > uiddate2) { uiddate2 = uid->created; uidnode2 = k; } else if (uid->created == uiddate2 && uidnode2) { if (cmp_user_ids (uid, uidnode2->pkt->pkt.user_id) > 0) uidnode2 = k; } } } } if (uidnode) { for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (k != uidnode) uid->is_primary = 0; } } } else if (uidnode2) { /* None is flagged primary - use the latest user ID we have, and disambiguate with the arbitrary packet comparison. */ uidnode2->pkt->pkt.user_id->is_primary = 1; } else { /* None of our uids were self-signed, so pick the one that sorts first to be the primary. This is the best we can do here since there are no self sigs to date the uids. */ uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { if (!uidnode) { uidnode = k; uidnode->pkt->pkt.user_id->is_primary = 1; continue; } else { if (cmp_user_ids (k->pkt->pkt.user_id, uidnode->pkt->pkt.user_id) > 0) { uidnode->pkt->pkt.user_id->is_primary = 0; uidnode = k; uidnode->pkt->pkt.user_id->is_primary = 1; } else k->pkt->pkt.user_id->is_primary = 0; /* just to be safe */ } } } } } /* Convert a buffer to a signature. Useful for 0x19 embedded sigs. Caller must free the signature when they are done. */ static PKT_signature * buf_to_sig (const byte * buf, size_t len) { PKT_signature *sig = xmalloc_clear (sizeof (PKT_signature)); IOBUF iobuf = iobuf_temp_with_content (buf, len); int save_mode = set_packet_list_mode (0); if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0) { xfree (sig); sig = NULL; } set_packet_list_mode (save_mode); iobuf_close (iobuf); return sig; } /* Use the self-signed data to fill in various fields in subkeys. KEYBLOCK is the whole keyblock. SUBNODE is the subkey to fill in. Sets the following fields on the subkey: main_keyid flags.valid if the subkey has a valid self-sig binding flags.revoked flags.backsig pubkey_usage has_expired expired_date On this subkey's most revent valid self-signed packet, the following field is set: flags.chosen_selfsig */ static void merge_selfsigs_subkey (KBNODE keyblock, KBNODE subnode) { PKT_public_key *mainpk = NULL, *subpk = NULL; PKT_signature *sig; KBNODE k; u32 mainkid[2]; u32 sigdate = 0; KBNODE signode; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; if (subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY) BUG (); mainpk = keyblock->pkt->pkt.public_key; if (mainpk->version < 4) return;/* (actually this should never happen) */ keyid_from_pk (mainpk, mainkid); subpk = subnode->pkt->pkt.public_key; keytimestamp = subpk->timestamp; subpk->flags.valid = 0; subpk->flags.exact = 0; subpk->main_keyid[0] = mainpk->main_keyid[0]; subpk->main_keyid[1] = mainpk->main_keyid[1]; /* Find the latest key binding self-signature. */ signode = NULL; sigdate = 0; /* Helper to find the latest signature. */ for (k = subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { sig = k->pkt->pkt.signature; if (sig->keyid[0] == mainkid[0] && sig->keyid[1] == mainkid[1]) { if (check_key_signature (keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_SUBKEY_REV (sig)) { /* Note that this means that the date on a revocation sig does not matter - even if the binding sig is dated after the revocation sig, the subkey is still marked as revoked. This seems ok, as it is just as easy to make new subkeys rather than re-sign old ones as the problem is in the distribution. Plus, PGP (7) does this the same way. */ subpk->flags.revoked = 1; sig_to_revoke_info (sig, &subpk->revoked); /* Although we could stop now, we continue to * figure out other information like the old expiration * time. */ } else if (IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate) { if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; } } } } } /* No valid key binding. */ if (!signode) return; sig = signode->pkt->pkt.signature; sig->flags.chosen_selfsig = 1; /* So we know which selfsig we chose later. */ key_usage = parse_key_usage (sig); if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (subpk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (subpk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } subpk->pubkey_usage = key_usage; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) key_expire = keytimestamp + buf32_to_u32 (p); else key_expire = 0; subpk->has_expired = key_expire >= curtime ? 0 : key_expire; subpk->expiredate = key_expire; /* Algo doesn't exist. */ if (openpgp_pk_test_algo (subpk->pubkey_algo)) return; subpk->flags.valid = 1; /* Find the most recent 0x19 embedded signature on our self-sig. */ if (!subpk->flags.backsig) { int seq = 0; size_t n; PKT_signature *backsig = NULL; sigdate = 0; /* We do this while() since there may be other embedded signatures in the future. We only want 0x19 here. */ while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } seq = 0; /* It is safe to have this in the unhashed area since the 0x19 is located on the selfsig for convenience, not security. */ while ((p = enum_sig_subpkt (sig->unhashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } if (backsig) { /* At this point, backsig contains the most recent 0x19 sig. Let's see if it is good. */ /* 2==valid, 1==invalid, 0==didn't check */ if (check_backsig (mainpk, subpk, backsig) == 0) subpk->flags.backsig = 2; else subpk->flags.backsig = 1; free_seckey_enc (backsig); } } } /* Merge information from the self-signatures with the public key, subkeys and user ids to make using them more easy. See documentation for merge_selfsigs_main, merge_selfsigs_subkey and fixup_uidnode for exactly which fields are updated. */ static void merge_selfsigs (KBNODE keyblock) { KBNODE k; int revoked; struct revoke_info rinfo; PKT_public_key *main_pk; prefitem_t *prefs; unsigned int mdc_feature; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) { log_error ("expected public key but found secret key " "- must stop\n"); /* We better exit here because a public key is expected at other places too. FIXME: Figure this out earlier and don't get to here at all */ g10_exit (1); } BUG (); } merge_selfsigs_main (keyblock, &revoked, &rinfo); /* Now merge in the data from each of the subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { merge_selfsigs_subkey (keyblock, k); } } main_pk = keyblock->pkt->pkt.public_key; if (revoked || main_pk->has_expired || !main_pk->flags.valid) { /* If the primary key is revoked, expired, or invalid we * better set the appropriate flags on that key and all * subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (!main_pk->flags.valid) pk->flags.valid = 0; if (revoked && !pk->flags.revoked) { pk->flags.revoked = revoked; memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); } if (main_pk->has_expired) pk->has_expired = main_pk->has_expired; } } return; } /* Set the preference list of all keys to those of the primary real * user ID. Note: we use these preferences when we don't know by * which user ID the key has been selected. * fixme: we should keep atoms of commonly used preferences or * use reference counting to optimize the preference lists storage. * FIXME: it might be better to use the intersection of * all preferences. * Do a similar thing for the MDC feature flag. */ prefs = NULL; mdc_feature = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { prefs = k->pkt->pkt.user_id->prefs; mdc_feature = k->pkt->pkt.user_id->flags.mdc; break; } } for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (pk->prefs) xfree (pk->prefs); pk->prefs = copy_prefs (prefs); pk->flags.mdc = mdc_feature; } } } /* See whether the key satisfies any additional requirements specified * in CTX. If so, return the node of an appropriate key or subkey. * Otherwise, return NULL if there was no appropriate key. * * Note that we do not return a reference, i.e. the result must not be * freed using 'release_kbnode'. * * In case the primary key is not required, select a suitable subkey. * We need the primary key if PUBKEY_USAGE_CERT is set in REQ_USAGE or * we are in PGP6 or PGP7 mode and PUBKEY_USAGE_SIG is set in * REQ_USAGE. * * If any of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT * are set in REQ_USAGE, we filter by the key's function. Concretely, * if PUBKEY_USAGE_SIG and PUBKEY_USAGE_CERT are set, then we only * return a key if it is (at least) either a signing or a * certification key. * * If REQ_USAGE is set, then we reject any keys that are not good * (i.e., valid, not revoked, not expired, etc.). This allows the * getkey functions to be used for plain key listings. * * Sets the matched key's user id field (pk->user_id) to the user id * that matched the low-level search criteria or NULL. * * If R_FLAGS is not NULL set certain flags for more detailed error * reporting. Used flags are: * * - LOOKUP_ALL_SUBKEYS_EXPIRED :: All Subkeys are expired or have * been revoked. * - LOOKUP_NOT_SELECTED :: No suitable key found * * This function needs to handle several different cases: * * 1. No requested usage and no primary key requested * Examples for this case are that we have a keyID to be used * for decrytion or verification. * 2. No usage but primary key requested * This is the case for all functions which work on an * entire keyblock, e.g. for editing or listing * 3. Usage and primary key requested * FIXME * 4. Usage but no primary key requested * FIXME * */ static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, unsigned int *r_flags) { kbnode_t k; /* If WANT_EXACT is set, the key or subkey that actually matched the low-level search criteria. */ kbnode_t foundk = NULL; /* The user id (if any) that matched the low-level search criteria. */ PKT_user_id *foundu = NULL; u32 latest_date; kbnode_t latest_key; PKT_public_key *pk; int req_prim; u32 curtime = make_timestamp (); if (r_flags) *r_flags = 0; #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT) req_usage &= USAGE_MASK; /* Request the primary if we're certifying another key, and also if * signing data while --pgp6 or --pgp7 is on since pgp 6 and 7 do * not understand signatures made by a signing subkey. PGP 8 does. */ req_prim = ((req_usage & PUBKEY_USAGE_CERT) || ((PGP6 || PGP7) && (req_usage & PUBKEY_USAGE_SIG))); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* For an exact match mark the primary or subkey that matched the low-level search criteria. */ if (want_exact) { for (k = keyblock; k; k = k->next) { if ((k->flag & 1)) { log_assert (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY); foundk = k; pk = k->pkt->pkt.public_key; pk->flags.exact = 1; break; } } } /* Get the user id that matched that low-level search criteria. */ for (k = keyblock; k; k = k->next) { if ((k->flag & 2)) { log_assert (k->pkt->pkttype == PKT_USER_ID); foundu = k->pkt->pkt.user_id; break; } } if (DBG_LOOKUP) log_debug ("finish_lookup: checking key %08lX (%s)(req_usage=%x)\n", (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL), foundk ? "one" : "all", req_usage); if (!req_usage) { latest_key = foundk ? foundk : keyblock; goto found; } latest_date = 0; latest_key = NULL; /* Set LATEST_KEY to the latest (the one with the most recent * timestamp) good (valid, not revoked, not expired, etc.) subkey. * * Don't bother if we are only looking for a primary key or we need * an exact match and the exact match is not a subkey. */ if (req_prim || (foundk && foundk->pkt->pkttype != PKT_PUBLIC_SUBKEY)) ; else { kbnode_t nextk; int n_subkeys = 0; int n_revoked_or_expired = 0; /* Either start a loop or check just this one subkey. */ for (k = foundk ? foundk : keyblock; k; k = nextk) { if (foundk) { /* If FOUNDK is not NULL, then only consider that exact key, i.e., don't iterate. */ nextk = NULL; } else nextk = k->next; if (k->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = k->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tsubkey not valid\n"); continue; } if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_LOOKUP) log_debug ("\tusage does not match: want=%x have=%x\n", req_usage, pk->pubkey_usage); continue; } n_subkeys++; if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tsubkey has been revoked\n"); n_revoked_or_expired++; continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tsubkey has expired\n"); n_revoked_or_expired++; continue; } if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tsubkey not yet valid\n"); continue; } if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure that it is used. A better change would be to compare ">=" but that might also change the selected keys and is as such a more intrusive change. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; latest_key = k; } } if (n_subkeys == n_revoked_or_expired && r_flags) *r_flags |= LOOKUP_ALL_SUBKEYS_EXPIRED; } /* Check if the primary key is ok (valid, not revoke, not expire, * matches requested usage) if: * * - we didn't find an appropriate subkey and we're not doing an * exact search, * * - we're doing an exact match and the exact match was the * primary key, or, * * - we're just considering the primary key. */ if ((!latest_key && !want_exact) || foundk == keyblock || req_prim) { if (DBG_LOOKUP && !foundk && !req_prim) log_debug ("\tno suitable subkeys found - trying primary\n"); pk = keyblock->pkt->pkt.public_key; if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tprimary key not valid\n"); } else if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_LOOKUP) log_debug ("\tprimary key usage does not match: " "want=%x have=%x\n", req_usage, pk->pubkey_usage); } else if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tprimary key has been revoked\n"); } else if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } else /* Okay. */ { if (DBG_LOOKUP) log_debug ("\tprimary key may be used\n"); latest_key = keyblock; } } if (!latest_key) { if (DBG_LOOKUP) log_debug ("\tno suitable key found - giving up\n"); if (r_flags) *r_flags |= LOOKUP_NOT_SELECTED; return NULL; /* Not found. */ } found: if (DBG_LOOKUP) log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (latest_key->pkt->pkt.public_key, NULL)); if (latest_key) { pk = latest_key->pkt->pkt.public_key; free_user_id (pk->user_id); pk->user_id = scopy_user_id (foundu); } if (latest_key != keyblock && opt.verbose) { char *tempkeystr = xstrdup (keystr_from_pk (latest_key->pkt->pkt.public_key)); log_info (_("using subkey %s instead of primary key %s\n"), tempkeystr, keystr_from_pk (keyblock->pkt->pkt.public_key)); xfree (tempkeystr); } cache_user_id (keyblock); return latest_key ? latest_key : keyblock; /* Found. */ } /* Print a KEY_CONSIDERED status line. */ static void print_status_key_considered (kbnode_t keyblock, unsigned int flags) { char hexfpr[2*MAX_FINGERPRINT_LEN + 1]; kbnode_t node; char flagbuf[20]; if (!is_status_enabled ()) return; for (node=keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) break; if (!node) { log_error ("%s: keyblock w/o primary key\n", __func__); return; } hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr); snprintf (flagbuf, sizeof flagbuf, " %u", flags); write_status_strings (STATUS_KEY_CONSIDERED, hexfpr, flagbuf, NULL); } /* A high-level function to lookup keys. This function builds on top of the low-level keydb API. It first searches the database using the description stored in CTX->ITEMS, then it filters the results using CTX and, finally, if WANT_SECRET is set, it ignores any keys for which no secret key is available. Unlike the low-level search functions, this function also merges all of the self-signed data into the keys, subkeys and user id packets (see the merge_selfsigs for details). On success the key's keyblock is stored at *RET_KEYBLOCK, and the specific subkey is stored at *RET_FOUND_KEY. Note that we do not return a reference in *RET_FOUND_KEY, i.e. the result must not be freed using 'release_kbnode', and it is only valid until *RET_KEYBLOCK is deallocated. Therefore, if RET_FOUND_KEY is not NULL, then RET_KEYBLOCK must not be NULL. */ static int lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, kbnode_t *ret_found_key, int want_secret) { int rc; int no_suitable_key = 0; KBNODE keyblock = NULL; KBNODE found_key = NULL; unsigned int infoflags; log_assert (ret_found_key == NULL || ret_keyblock != NULL); if (ret_keyblock) *ret_keyblock = NULL; for (;;) { rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems, NULL); if (rc) break; /* If we are iterating over the entire database, then we need to change from KEYDB_SEARCH_MODE_FIRST, which does an implicit reset, to KEYDB_SEARCH_MODE_NEXT, which gets the next record. */ if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST) ctx->items->mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_keyblock (ctx->kr_handle, &keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto skip; } if (want_secret && agent_probe_any_secret_key (NULL, keyblock)) goto skip; /* No secret key available. */ /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. */ merge_selfsigs (keyblock); found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) { no_suitable_key = 0; goto found; } else { no_suitable_key = 1; } skip: /* Release resources and continue search. */ release_kbnode (keyblock); keyblock = NULL; /* The keyblock cache ignores the current "file position". Thus, if we request the next result and the cache matches (and it will since it is what we just looked for), we'll get the same entry back! We can avoid this infinite loop by disabling the cache. */ keydb_disable_caching (ctx->kr_handle); } found: if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); if (!rc) { if (ret_keyblock) { *ret_keyblock = keyblock; /* Return the keyblock. */ keyblock = NULL; } } else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && no_suitable_key) rc = want_secret? GPG_ERR_UNUSABLE_SECKEY : GPG_ERR_UNUSABLE_PUBKEY; else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = want_secret? GPG_ERR_NO_SECKEY : GPG_ERR_NO_PUBKEY; release_kbnode (keyblock); if (ret_found_key) { if (! rc) *ret_found_key = found_key; else *ret_found_key = NULL; } return rc; } /* Enumerate some secret keys (specifically, those specified with * --default-key and --try-secret-key). Use the following procedure: * * 1) Initialize a void pointer to NULL * 2) Pass a reference to this pointer to this function (content) * and provide space for the secret key (sk) * 3) Call this function as long as it does not return an error (or * until you are done). The error code GPG_ERR_EOF indicates the * end of the listing. * 4) Call this function a last time with SK set to NULL, * so that can free it's context. * * In pseudo-code: * * void *ctx = NULL; * PKT_public_key *sk = xmalloc_clear (sizeof (*sk)); * * while ((err = enum_secret_keys (&ctx, sk))) * { // Process SK. * if (done) * break; * free_public_key (sk); * sk = xmalloc_clear (sizeof (*sk)); * } * * // Release any resources used by CTX. * enum_secret_keys (&ctx, NULL); * free_public_key (sk); * * if (gpg_err_code (err) != GPG_ERR_EOF) * ; // An error occurred. */ gpg_error_t enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) { gpg_error_t err = 0; const char *name; kbnode_t keyblock; struct { int eof; int state; strlist_t sl; kbnode_t keyblock; kbnode_t node; getkey_ctx_t ctx; } *c = *context; if (!c) { /* Make a new context. */ c = xtrycalloc (1, sizeof *c); if (!c) return gpg_error_from_syserror (); *context = c; } if (!sk) { /* Free the context. */ release_kbnode (c->keyblock); getkey_end (c->ctx); xfree (c); *context = NULL; return 0; } if (c->eof) return gpg_error (GPG_ERR_EOF); for (;;) { /* Loop until we have a keyblock. */ while (!c->keyblock) { /* Loop over the list of secret keys. */ do { name = NULL; keyblock = NULL; switch (c->state) { case 0: /* First try to use the --default-key. */ name = parse_def_secret_key (ctrl); c->state = 1; break; case 1: /* Init list of keys to try. */ c->sl = opt.secret_keys_to_try; c->state++; break; case 2: /* Get next item from list. */ if (c->sl) { name = c->sl->d; c->sl = c->sl->next; } else c->state++; break; case 3: /* Init search context to enum all secret keys. */ err = getkey_bynames (&c->ctx, NULL, NULL, 1, &keyblock); if (err) { release_kbnode (keyblock); keyblock = NULL; getkey_end (c->ctx); c->ctx = NULL; } c->state++; break; case 4: /* Get next item from the context. */ if (c->ctx) { err = getkey_next (c->ctx, NULL, &keyblock); if (err) { release_kbnode (keyblock); keyblock = NULL; getkey_end (c->ctx); c->ctx = NULL; } } else c->state++; break; default: /* No more names to check - stop. */ c->eof = 1; return gpg_error (GPG_ERR_EOF); } } while ((!name || !*name) && !keyblock); if (keyblock) c->node = c->keyblock = keyblock; else { err = getkey_byname (ctrl, NULL, NULL, name, 1, &c->keyblock); if (err) { /* getkey_byname might return a keyblock even in the error case - I have not checked. Thus better release it. */ release_kbnode (c->keyblock); c->keyblock = NULL; } else c->node = c->keyblock; } } /* Get the next key from the current keyblock. */ for (; c->node; c->node = c->node->next) { if (c->node->pkt->pkttype == PKT_PUBLIC_KEY || c->node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { copy_public_key (sk, c->node->pkt->pkt.public_key); c->node = c->node->next; return 0; /* Found. */ } } /* Dispose the keyblock and continue. */ release_kbnode (c->keyblock); c->keyblock = NULL; } } /********************************************* *********** User ID printing helpers ******* *********************************************/ /* Return a string with a printable representation of the user_id. * this string must be freed by xfree. */ static char * get_user_id_string (u32 * keyid, int mode, size_t *r_len) { user_id_db_t r; keyid_list_t a; int pass = 0; char *p; /* Try it two times; second pass reads from the database. */ do { for (r = user_id_db; r; r = r->next) { for (a = r->keyids; a; a = a->next) { if (a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1]) { if (mode == 2) { /* An empty string as user id is possible. Make sure that the malloc allocates one byte and does not bail out. */ p = xmalloc (r->len? r->len : 1); memcpy (p, r->name, r->len); if (r_len) *r_len = r->len; } else { if (mode) p = xasprintf ("%08lX%08lX %.*s", (ulong) keyid[0], (ulong) keyid[1], r->len, r->name); else p = xasprintf ("%s %.*s", keystr (keyid), r->len, r->name); if (r_len) *r_len = strlen (p); } return p; } } } } while (++pass < 2 && !get_pubkey (NULL, keyid)); if (mode == 2) p = xstrdup (user_id_not_found_utf8 ()); else if (mode) p = xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]); else p = xasprintf ("%s [?]", keystr (keyid)); if (r_len) *r_len = strlen (p); return p; } char * get_user_id_string_native (u32 * keyid) { char *p = get_user_id_string (keyid, 0, NULL); char *p2 = utf8_to_native (p, strlen (p), 0); xfree (p); return p2; } char * get_long_user_id_string (u32 * keyid) { return get_user_id_string (keyid, 1, NULL); } /* Please try to use get_user_byfpr instead of this one. */ char * get_user_id (u32 * keyid, size_t * rn) { return get_user_id_string (keyid, 2, rn); } /* Please try to use get_user_id_byfpr_native instead of this one. */ char * get_user_id_native (u32 * keyid) { size_t rn; char *p = get_user_id (keyid, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } /* Return the user id for a key designated by its fingerprint, FPR, which must be MAX_FINGERPRINT_LEN bytes in size. Note: the returned string, which must be freed using xfree, may not be NUL terminated. To determine the length of the string, you must use *RN. */ char * get_user_id_byfpr (const byte *fpr, size_t *rn) { user_id_db_t r; char *p; int pass = 0; /* Try it two times; second pass reads from the database. */ do { for (r = user_id_db; r; r = r->next) { keyid_list_t a; for (a = r->keyids; a; a = a->next) { if (!memcmp (a->fpr, fpr, MAX_FINGERPRINT_LEN)) { /* An empty string as user id is possible. Make sure that the malloc allocates one byte and does not bail out. */ p = xmalloc (r->len? r->len : 1); memcpy (p, r->name, r->len); *rn = r->len; return p; } } } } while (++pass < 2 && !get_pubkey_byfprint (NULL, NULL, fpr, MAX_FINGERPRINT_LEN)); p = xstrdup (user_id_not_found_utf8 ()); *rn = strlen (p); return p; } /* Like get_user_id_byfpr, but convert the string to the native encoding. The returned string needs to be freed. Unlike get_user_id_byfpr, the returned string is NUL terminated. */ char * get_user_id_byfpr_native (const byte *fpr) { size_t rn; char *p = get_user_id_byfpr (fpr, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } /* Return the database handle used by this context. The context still owns the handle. */ KEYDB_HANDLE get_ctx_handle (GETKEY_CTX ctx) { return ctx->kr_handle; } static void free_akl (struct akl *akl) { if (! akl) return; if (akl->spec) free_keyserver_spec (akl->spec); xfree (akl); } void release_akl (void) { while (opt.auto_key_locate) { struct akl *akl2 = opt.auto_key_locate; opt.auto_key_locate = opt.auto_key_locate->next; free_akl (akl2); } } /* Returns false on error. */ int parse_auto_key_locate (char *options) { char *tok; while ((tok = optsep (&options))) { struct akl *akl, *check, *last = NULL; int dupe = 0; if (tok[0] == '\0') continue; akl = xmalloc_clear (sizeof (*akl)); if (ascii_strcasecmp (tok, "clear") == 0) { xfree (akl); free_akl (opt.auto_key_locate); opt.auto_key_locate = NULL; continue; } else if (ascii_strcasecmp (tok, "nodefault") == 0) akl->type = AKL_NODEFAULT; else if (ascii_strcasecmp (tok, "local") == 0) akl->type = AKL_LOCAL; else if (ascii_strcasecmp (tok, "ldap") == 0) akl->type = AKL_LDAP; else if (ascii_strcasecmp (tok, "keyserver") == 0) akl->type = AKL_KEYSERVER; #ifdef USE_DNS_CERT else if (ascii_strcasecmp (tok, "cert") == 0) akl->type = AKL_CERT; #endif else if (ascii_strcasecmp (tok, "pka") == 0) akl->type = AKL_PKA; else if (ascii_strcasecmp (tok, "dane") == 0) akl->type = AKL_DANE; else if (ascii_strcasecmp (tok, "wkd") == 0) akl->type = AKL_WKD; else if ((akl->spec = parse_keyserver_uri (tok, 1))) akl->type = AKL_SPEC; else { free_akl (akl); return 0; } /* We must maintain the order the user gave us */ for (check = opt.auto_key_locate; check; last = check, check = check->next) { /* Check for duplicates */ if (check->type == akl->type && (akl->type != AKL_SPEC || (akl->type == AKL_SPEC && strcmp (check->spec->uri, akl->spec->uri) == 0))) { dupe = 1; free_akl (akl); break; } } if (!dupe) { if (last) last->next = akl; else opt.auto_key_locate = akl; } } return 1; } /* Returns true if a secret key is available for the public key with key id KEYID; returns false if not. This function ignores legacy keys. Note: this is just a fast check and does not tell us whether the secret key is valid; this check merely indicates whether there is some secret key with the specified key id. */ int have_secret_key_with_kid (u32 *keyid) { gpg_error_t err; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; kbnode_t keyblock; kbnode_t node; int result = 0; kdbhd = keydb_new (); if (!kdbhd) return 0; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = keyid[0]; desc.u.kid[1] = keyid[1]; while (!result) { err = keydb_search (kdbhd, &desc, 1, NULL); if (err) break; err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); break; } for (node = keyblock; node; node = node->next) { /* Bit 0 of the flags is set if the search found the key using that key or subkey. Note: a search will only ever match a single key or subkey. */ if ((node->flag & 1)) { log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (!agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) result = 1; /* Secret key available. */ else result = 0; break; } } release_kbnode (keyblock); } keydb_release (kdbhd); return result; } diff --git a/g10/gpgv.c b/g10/gpgv.c index da07989ea..0ecf232e2 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -1,724 +1,727 @@ /* gpgv.c - The GnuPG signature verify utility * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2006, * 2008, 2009, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM #include /* for setmode() */ #endif #ifdef HAVE_LIBREADLINE #define GNUPG_LIBREADLINE_H_INCLUDED #include #endif #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" #include "sysutils.h" #include "status.h" #include "call-agent.h" #include "../common/init.h" enum cmd_and_opt_values { aNull = 0, oQuiet = 'q', oVerbose = 'v', oOutput = 'o', oBatch = 500, oKeyring, oIgnoreTimeConflict, oStatusFD, oLoggerFD, oHomedir, oWeakDigest, oEnableSpecialFilenames, aTest }; static ARGPARSE_OPTS opts[] = { ARGPARSE_group (300, N_("@\nOptions:\n ")), ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), ARGPARSE_s_s (oKeyring, "keyring", N_("|FILE|take the keys from the keyring FILE")), ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", N_("make timestamp conflicts only a warning")), ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_s (oWeakDigest, "weak-digest", N_("|ALGO|reject signatures made with ALGO")), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_end () }; int g10_errors_seen = 0; static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { const char *s; char *result; s = getfnc (NULL); result = xmalloc (strlen (libname) + 1 + strlen (s) + 1); strcpy (stpcpy (stpcpy (result, libname), " "), s); return result; } static const char * my_strusage( int level ) { static char *ver_gcry; const char *p; switch (level) { case 11: p = "@GPG@v (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: gpgv [options] [files] (-h for help)"); break; case 41: p = _("Syntax: gpgv [options] [files]\n" "Check signatures against known trusted keys\n"); break; case 20: if (!ver_gcry) ver_gcry = make_libversion ("libgcrypt", gcry_check_version); p = ver_gcry; break; default: p = NULL; } return p; } int main( int argc, char **argv ) { ARGPARSE_ARGS pargs; int rc=0; strlist_t sl; strlist_t nrings = NULL; unsigned configlineno; ctrl_t ctrl; early_system_init (); set_strusage (my_strusage); log_set_prefix ("gpgv", GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init(); init_common_subsystems (&argc, &argv); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gnupg_init_signals (0, NULL); opt.command_fd = -1; /* no command fd */ opt.keyserver_options.options |= KEYSERVER_AUTO_KEY_RETRIEVE; opt.trust_model = TM_ALWAYS; opt.no_sig_cache = 1; opt.flags.require_cross_cert = 1; opt.batch = 1; opt.answer_yes = 1; opt.weak_digests = NULL; tty_no_terminal(1); tty_batchmode(1); dotlock_disable (); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); additional_weak_digest("MD5"); pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ while (optfile_parse( NULL, NULL, &configlineno, &pargs, opts)) { switch (pargs.r_opt) { case oQuiet: opt.quiet = 1; break; case oVerbose: opt.verbose++; opt.list_sigs=1; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); break; case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oStatusFD: set_status_fd( pargs.r.ret_int ); break; case oLoggerFD: log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; case oWeakDigest: additional_weak_digest(pargs.r.ret_str); break; case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; case oEnableSpecialFilenames: iobuf_enable_special_filenames (1); break; default : pargs.err = ARGPARSE_PRINT_ERROR; break; } } if (log_get_errorcount (0)) g10_exit(2); if (opt.verbose > 1) set_packet_list_mode(1); /* Note: We open all keyrings in read-only mode. */ if (!nrings) /* No keyring given: use default one. */ keydb_add_resource ("trustedkeys" EXTSEP_S "kbx", (KEYDB_RESOURCE_FLAG_READONLY |KEYDB_RESOURCE_FLAG_GPGVDEF)); for (sl = nrings; sl; sl = sl->next) keydb_add_resource (sl->d, KEYDB_RESOURCE_FLAG_READONLY); FREE_STRLIST (nrings); ctrl = xcalloc (1, sizeof *ctrl); if ((rc = verify_signatures (ctrl, argc, argv))) log_error("verify signatures failed: %s\n", gpg_strerror (rc) ); xfree (ctrl); /* cleanup */ g10_exit (0); return 8; /*NOTREACHED*/ } void g10_exit( int rc ) { rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; exit(rc ); } /* Stub: * We have to override the trustcheck from pkclist.c because * this utility assumes that all keys in the keyring are trustworthy */ int check_signatures_trust (ctrl_t ctrl, PKT_signature *sig) { (void)ctrl; (void)sig; return 0; } void read_trust_options(byte *trust_model, ulong *created, ulong *nextcheck, byte *marginals, byte *completes, byte *cert_depth, byte *min_cert_level) { (void)trust_model; (void)created; (void)nextcheck; (void)marginals; (void)completes; (void)cert_depth; (void)min_cert_level; } /* Stub: * We don't have the trustdb , so we have to provide some stub functions * instead */ int cache_disabled_value(PKT_public_key *pk) { (void)pk; return 0; } void check_trustdb_stale (ctrl_t ctrl) { (void)ctrl; } int -get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) +get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, + PKT_user_id *uid) { (void)ctrl; + (void)kb; (void)pk; (void)uid; return '?'; } unsigned int -get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid, +get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig, int may_ask) { (void)ctrl; + (void)kb; (void)pk; (void)uid; (void)sig; (void)may_ask; return 0; } const char * trust_value_to_string (unsigned int value) { (void)value; return "err"; } const char * uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid) { (void)ctrl; (void)key; (void)uid; return "err"; } int get_ownertrust_info (PKT_public_key *pk) { (void)pk; return '?'; } unsigned int get_ownertrust (PKT_public_key *pk) { (void)pk; return TRUST_UNKNOWN; } /* Stubs: * Because we only work with trusted keys, it does not make sense to * get them from a keyserver */ struct keyserver_spec * keyserver_match (struct keyserver_spec *spec) { (void)spec; return NULL; } int keyserver_any_configured (ctrl_t ctrl) { (void)ctrl; return 0; } int keyserver_import_keyid (u32 *keyid, void *dummy, int quick) { (void)keyid; (void)dummy; (void)quick; return -1; } int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, struct keyserver_spec *keyserver, int quick) { (void)ctrl; (void)fprint; (void)fprint_len; (void)keyserver; (void)quick; return -1; } int keyserver_import_cert (const char *name) { (void)name; return -1; } int keyserver_import_pka (const char *name,unsigned char *fpr) { (void)name; (void)fpr; return -1; } gpg_error_t keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, unsigned char **fpr, size_t *fpr_len) { (void)ctrl; (void)name; (void)quick; (void)fpr; (void)fpr_len; return GPG_ERR_BUG; } int keyserver_import_name (const char *name,struct keyserver_spec *spec) { (void)name; (void)spec; return -1; } int keyserver_import_ldap (const char *name) { (void)name; return -1; } gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock) { (void)ctrl; (void)fname; (void)r_keyblock; return -1; } /* Stub: * No encryption here but mainproc links to these functions. */ gpg_error_t get_session_key (ctrl_t ctrl, PKT_pubkey_enc *k, DEK *dek) { (void)ctrl; (void)k; (void)dek; return GPG_ERR_GENERAL; } /* Stub: */ gpg_error_t get_override_session_key (DEK *dek, const char *string) { (void)dek; (void)string; return GPG_ERR_GENERAL; } /* Stub: */ int decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) { (void)ctrl; (void)procctx; (void)ed; (void)dek; return GPG_ERR_GENERAL; } /* Stub: * No interactive commands, so we don't need the helptexts */ void display_online_help (const char *keyword) { (void)keyword; } /* Stub: * We don't use secret keys, but getkey.c links to this */ int check_secret_key (PKT_public_key *pk, int n) { (void)pk; (void)n; return GPG_ERR_GENERAL; } /* Stub: * No secret key, so no passphrase needed */ DEK * passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache, const char *tmp, int *canceled) { (void)cipher_algo; (void)s2k; (void)create; (void)nocache; (void)tmp; if (canceled) *canceled = 0; return NULL; } void passphrase_clear_cache (const char *cacheid) { (void)cacheid; } struct keyserver_spec * parse_preferred_keyserver(PKT_signature *sig) { (void)sig; return NULL; } struct keyserver_spec * parse_keyserver_uri (const char *uri, int require_scheme, const char *configname, unsigned int configlineno) { (void)uri; (void)require_scheme; (void)configname; (void)configlineno; return NULL; } void free_keyserver_spec (struct keyserver_spec *keyserver) { (void)keyserver; } /* Stubs to avoid linking to photoid.c */ void show_photos (const struct user_attribute *attrs, int count, PKT_public_key *pk) { (void)attrs; (void)count; (void)pk; } int parse_image_header (const struct user_attribute *attr, byte *type, u32 *len) { (void)attr; (void)type; (void)len; return 0; } char * image_type_to_string (byte type, int string) { (void)type; (void)string; return NULL; } #ifdef ENABLE_CARD_SUPPORT int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { (void)name; (void)info; return 0; } #endif /* ENABLE_CARD_SUPPORT */ /* We do not do any locking, so use these stubs here */ void dotlock_disable (void) { } dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags) { (void)file_to_lock; (void)flags; return NULL; } void dotlock_destroy (dotlock_t h) { (void)h; } int dotlock_take (dotlock_t h, long timeout) { (void)h; (void)timeout; return 0; } int dotlock_release (dotlock_t h) { (void)h; return 0; } void dotlock_remove_lockfiles (void) { } gpg_error_t agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { (void)ctrl; (void)pk; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { (void)ctrl; (void)keyblock; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno, int *r_cleartext) { (void)ctrl; (void)hexkeygrip; (void)r_cleartext; *r_serialno = NULL; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, unsigned char **r_fpr, size_t *r_fprlen, char **r_url) { (void)ctrl; (void)userid; if (r_fpr) *r_fpr = NULL; if (r_fprlen) *r_fprlen = 0; if (r_url) *r_url = NULL; return gpg_error (GPG_ERR_NOT_FOUND); } gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen) { (void)ctrl; (void)keyspec; (void)options; (void)stats; *r_keyblock = NULL; *r_data = NULL; *r_datalen = 0; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } gpg_error_t tofu_write_tfs_record (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, const char *user_id) { (void)ctrl; (void)fp; (void)pk; (void)user_id; return gpg_error (GPG_ERR_GENERAL); } gpg_error_t tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id, enum tofu_policy *policy) { (void)ctrl; (void)pk; (void)user_id; (void)policy; return gpg_error (GPG_ERR_GENERAL); } const char * tofu_policy_str (enum tofu_policy policy) { (void)policy; return "unknown"; } void tofu_begin_batch_update (ctrl_t ctrl) { (void)ctrl; } void tofu_end_batch_update (ctrl_t ctrl) { (void)ctrl; } gpg_error_t tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) { (void) ctrl; (void) kb; return 0; } diff --git a/g10/keyedit.c b/g10/keyedit.c index 5b77ee747..94fa8c49a 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,6555 +1,6556 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2016 Werner Koch * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #ifdef HAVE_LIBREADLINE # define GNUPG_LIBREADLINE_H_INCLUDED # include #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "iobuf.h" #include "keydb.h" #include "photoid.h" #include "util.h" #include "main.h" #include "trustdb.h" #include "filter.h" #include "ttyio.h" #include "status.h" #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "host2net.h" #include "tofu.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose); static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs); static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn); static void show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys); static void show_key_and_grip (kbnode_t keyblock); static void subkey_expire_warning (kbnode_t keyblock); static int menu_adduid (ctrl_t ctrl, kbnode_t keyblock, int photo, const char *photo_name, const char *uidstr); static void menu_deluid (KBNODE pub_keyblock); static int menu_delsig (KBNODE pub_keyblock); static int menu_clean (KBNODE keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); static int menu_expire (KBNODE pub_keyblock); static int menu_changeusage (kbnode_t keyblock); static int menu_backsign (KBNODE pub_keyblock); static int menu_set_primary_uid (KBNODE pub_keyblock); static int menu_set_preferences (KBNODE pub_keyblock); static int menu_set_keyserver_url (const char *url, KBNODE pub_keyblock); static int menu_set_notation (const char *string, KBNODE pub_keyblock); static int menu_select_uid (KBNODE keyblock, int idx); static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash); static int menu_select_key (KBNODE keyblock, int idx, char *p); static int count_uids (KBNODE keyblock); static int count_uids_with_flag (KBNODE keyblock, unsigned flag); static int count_keys_with_flag (KBNODE keyblock, unsigned flag); static int count_selected_uids (KBNODE keyblock); static int real_uids_left (KBNODE keyblock); static int count_selected_keys (KBNODE keyblock); static int menu_revsig (KBNODE keyblock); static int menu_revuid (ctrl_t ctrl, kbnode_t keyblock); static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified); static int menu_revkey (KBNODE pub_keyblock); static int menu_revsubkey (KBNODE pub_keyblock); #ifndef NO_TRUST_MODELS static int enable_disable_key (KBNODE keyblock, int disable); #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock); static int update_trust = 0; #define CONTROL_D ('D' - 'A' + 1) #define NODFLG_BADSIG (1<<0) /* Bad signature. */ #define NODFLG_NOKEY (1<<1) /* No public key. */ #define NODFLG_SIGERR (1<<2) /* Other sig error. */ #define NODFLG_MARK_A (1<<4) /* Temporary mark. */ #define NODFLG_DELSIG (1<<5) /* To be deleted. */ #define NODFLG_SELUID (1<<8) /* Indicate the selected userid. */ #define NODFLG_SELKEY (1<<9) /* Indicate the selected key. */ #define NODFLG_SELSIG (1<<10) /* Indicate a selected signature. */ struct sign_attrib { int non_exportable, non_revocable; struct revocation_reason_info *reason; byte trust_depth, trust_value; char *trust_regexp; }; /* TODO: Fix duplicated code between here and the check-sigs/list-sigs code in keylist.c. */ static int print_and_check_one_sig_colon (KBNODE keyblock, KBNODE node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key) { PKT_signature *sig = node->pkt->pkt.signature; int rc, sigrc; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ rc = check_key_signature (keyblock, node, is_selfsig); switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { es_printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:", sigrc, sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], (ulong) sig->timestamp, (ulong) sig->expiredate); if (sig->trust_depth || sig->trust_value) es_printf ("%d %d", sig->trust_depth, sig->trust_value); es_printf (":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_printf ("::%02x%c\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (opt.show_subpackets) print_subpackets_colon (sig); } return (sigrc == '!'); } /* * Print information about a signature (rc is its status), check it * and return true if the signature is okay. NODE must be a signature * packet. With EXTENDED set all possible signature list options will * always be printed. */ static int print_one_sig (int rc, KBNODE keyblock, KBNODE node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; int is_rev = sig->sig_class == 0x30; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { tty_printf ("%s%c%c %c%c%c%c%c%c %s %s", is_rev ? "rev" : "sig", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended ) tty_printf (" %s", expirestr_from_sig (sig)); tty_printf (" "); if (sigrc == '%') tty_printf ("[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (is_selfsig) { tty_printf (is_rev ? _("[revocation]") : _("[self-signature]")); if (extended && sig->flags.chosen_selfsig) tty_printf ("*"); } else { size_t n; char *p = get_user_id (sig->keyid, &n); tty_print_utf8_string2 (NULL, p, n, opt.screen_columns - keystrlen () - 26 - ((opt. list_options & LIST_SHOW_SIG_EXPIRE) ? 11 : 0)); xfree (p); } tty_printf ("\n"); if (sig->flags.policy_url && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended)) show_policy_url (sig, 3, 0); if (sig->flags.notation && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended)) show_notation (sig, 3, 0, ((opt. list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt. list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended)) show_keyserver_url (sig, 3, 0); if (extended) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; const unsigned char *s; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (s && *s) tty_printf (" [primary]\n"); s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (s && buf32_to_u32 (s)) tty_printf (" [expires: %s]\n", isotimestamp (pk->timestamp + buf32_to_u32 (s))); } } return (sigrc == '!'); } static int print_and_check_one_sig (KBNODE keyblock, KBNODE node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key, int extended) { int rc; rc = check_key_signature (keyblock, node, is_selfsig); return print_one_sig (rc, keyblock, node, inv_sigs, no_key, oth_err, *is_selfsig, print_without_key, extended); } /* Order two signatures. The actual ordering isn't important. Our goal is to ensure that identical signatures occur together. */ static int sig_comparison (const void *av, const void *bv) { const KBNODE an = *(const KBNODE *) av; const KBNODE bn = *(const KBNODE *) bv; const PKT_signature *a; const PKT_signature *b; int ndataa; int ndatab; int i; log_assert (an->pkt->pkttype == PKT_SIGNATURE); log_assert (bn->pkt->pkttype == PKT_SIGNATURE); a = an->pkt->pkt.signature; b = bn->pkt->pkt.signature; if (a->digest_algo < b->digest_algo) return -1; if (a->digest_algo > b->digest_algo) return 1; ndataa = pubkey_get_nsig (a->pubkey_algo); ndatab = pubkey_get_nsig (b->pubkey_algo); if (ndataa != ndatab) return (ndataa < ndatab)? -1 : 1; for (i = 0; i < ndataa; i ++) { int c = gcry_mpi_cmp (a->data[i], b->data[i]); if (c != 0) return c; } /* Okay, they are equal. */ return 0; } /* Perform a few sanity checks on a keyblock is okay and possibly repair some damage. Concretely: - Detect duplicate signatures and remove them. - Detect out of order signatures and relocate them (e.g., a sig over user id X located under subkey Y). Note: this function does not remove signatures that don't belong or components that are not signed! (Although it would be trivial to do so.) If ONLY_SELFSIGS is true, then this function only reorders self signatures (it still checks all signatures for duplicates, however). Returns 1 if the keyblock was modified, 0 otherwise. */ static int check_all_keysigs (KBNODE kb, int only_selected, int only_selfsigs) { gpg_error_t err; PKT_public_key *pk; KBNODE n, n_next, *n_prevp, n2; char *pending_desc = NULL; PKT_public_key *issuer; KBNODE last_printed_component; KBNODE current_component = NULL; int dups = 0; int missing_issuer = 0; int reordered = 0; int bad_signature = 0; int missing_selfsig = 0; int modified = 0; log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); pk = kb->pkt->pkt.public_key; /* First we look for duplicates. */ { int nsigs; kbnode_t *sigs; int i; int last_i; /* Count the sigs. */ for (nsigs = 0, n = kb; n; n = n->next) { if (is_deleted_kbnode (n)) continue; else if (n->pkt->pkttype == PKT_SIGNATURE) nsigs ++; } if (!nsigs) return 0; /* No signatures at all. */ /* Add them all to the SIGS array. */ sigs = xtrycalloc (nsigs, sizeof *sigs); if (!sigs) { log_error (_("error allocating memory: %s\n"), gpg_strerror (gpg_error_from_syserror ())); return 0; } i = 0; for (n = kb; n; n = n->next) { if (is_deleted_kbnode (n)) continue; if (n->pkt->pkttype != PKT_SIGNATURE) continue; sigs[i] = n; i ++; } log_assert (i == nsigs); qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); last_i = 0; for (i = 1; i < nsigs; i ++) { log_assert (sigs[last_i]); log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE); log_assert (sigs[i]); log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE); if (sig_comparison (&sigs[last_i], &sigs[i]) == 0) /* They are the same. Kill the latter. */ { if (DBG_PACKET) { PKT_signature *sig = sigs[i]->pkt->pkt.signature; log_debug ("Signature appears multiple times, " "deleting duplicate:\n"); log_debug (" sig: class 0x%x, issuer: %s," " timestamp: %s (%lld), digest: %02x %02x\n", sig->sig_class, keystr (sig->keyid), isotimestamp (sig->timestamp), (long long) sig->timestamp, sig->digest_start[0], sig->digest_start[1]); } /* Remove sigs[i] from the keyblock. */ { KBNODE z, *prevp; int to_kill = last_i; last_i = i; for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next) if (z == sigs[to_kill]) break; *prevp = sigs[to_kill]->next; sigs[to_kill]->next = NULL; release_kbnode (sigs[to_kill]); sigs[to_kill] = NULL; dups ++; modified = 1; } } else last_i = i; } xfree (sigs); } /* Make sure the sigs occur after the component (public key, subkey, user id) that they sign. */ issuer = NULL; last_printed_component = NULL; for (n_prevp = &kb, n = kb; n; /* If we moved n, then n_prevp is need valid. */ n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next) { PACKET *p; int processed_current_component; PKT_signature *sig; int rc; int dump_sig_params = 0; n_next = n->next; if (is_deleted_kbnode (n)) continue; p = n->pkt; if (issuer && issuer != pk) { free_public_key (issuer); issuer = NULL; } xfree (pending_desc); pending_desc = NULL; switch (p->pkttype) { case PKT_PUBLIC_KEY: log_assert (p->pkt.public_key == pk); if (only_selected && ! (n->flag & NODFLG_SELKEY)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("public key %s: timestamp: %s (%lld)\n", pk_keyid_str (pk), isotimestamp (pk->timestamp), (long long) pk->timestamp); current_component = n; break; case PKT_PUBLIC_SUBKEY: if (only_selected && ! (n->flag & NODFLG_SELKEY)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("subkey %s: timestamp: %s (%lld)\n", pk_keyid_str (p->pkt.public_key), isotimestamp (p->pkt.public_key->timestamp), (long long) p->pkt.public_key->timestamp); current_component = n; break; case PKT_USER_ID: if (only_selected && ! (n->flag & NODFLG_SELUID)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("user id: %s\n", p->pkt.user_id->attrib_data ? "[ photo id ]" : p->pkt.user_id->name); current_component = n; break; case PKT_SIGNATURE: if (! current_component) /* The current component is not selected, don't check the sigs under it. */ break; sig = n->pkt->pkt.signature; pending_desc = xasprintf (" sig: class: 0x%x, issuer: %s," " timestamp: %s (%lld), digest: %02x %02x", sig->sig_class, keystr (sig->keyid), isotimestamp (sig->timestamp), (long long) sig->timestamp, sig->digest_start[0], sig->digest_start[1]); if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0) issuer = pk; else /* Issuer is a different key. */ { if (only_selfsigs) continue; issuer = xmalloc (sizeof (*issuer)); err = get_pubkey (issuer, sig->keyid); if (err) { xfree (issuer); issuer = NULL; if (DBG_PACKET) { if (pending_desc) log_debug ("%s", pending_desc); log_debug (" Can't check signature allegedly" " issued by %s: %s\n", keystr (sig->keyid), gpg_strerror (err)); } missing_issuer ++; break; } } if ((err = openpgp_pk_test_algo (sig->pubkey_algo))) { if (DBG_PACKET && pending_desc) log_debug ("%s", pending_desc); tty_printf (_("can't check signature with unsupported" " public-key algorithm (%d): %s.\n"), sig->pubkey_algo, gpg_strerror (err)); break; } if ((err = openpgp_md_test_algo (sig->digest_algo))) { if (DBG_PACKET && pending_desc) log_debug ("%s", pending_desc); tty_printf (_("can't check signature with unsupported" " message-digest algorithm %d: %s.\n"), sig->digest_algo, gpg_strerror (err)); break; } /* We iterate over the keyblock. Most likely, the matching component is the current component so always try that first. */ processed_current_component = 0; for (n2 = current_component; n2; n2 = (processed_current_component ? n2->next : kb), processed_current_component = 1) if (is_deleted_kbnode (n2)) continue; else if (processed_current_component && n2 == current_component) /* Don't process it twice. */ continue; else { err = check_signature_over_key_or_uid (issuer, sig, kb, n2->pkt, NULL, NULL); if (! err) break; } /* n/sig is a signature and n2 is the component (public key, subkey or user id) that it signs, if any. current_component is that component that it appears to apply to (according to the ordering). */ if (current_component == n2) { if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Good signature over last key or uid!\n"); } rc = 0; } else if (n2) { log_assert (n2->pkt->pkttype == PKT_USER_ID || n2->pkt->pkttype == PKT_PUBLIC_KEY || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Good signature out of order!" " (Over %s (%d) '%s')\n", n2->pkt->pkttype == PKT_USER_ID ? "user id" : n2->pkt->pkttype == PKT_PUBLIC_SUBKEY ? "subkey" : "primary key", n2->pkt->pkttype, n2->pkt->pkttype == PKT_USER_ID ? n2->pkt->pkt.user_id->name : pk_keyid_str (n2->pkt->pkt.public_key)); } /* Reorder the packets: move the signature n to be just after n2. */ /* Unlink the signature. */ log_assert (n_prevp); *n_prevp = n->next; /* Insert the sig immediately after the component. */ n->next = n2->next; n2->next = n; reordered ++; modified = 1; rc = 0; } else { if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Bad signature.\n"); } if (DBG_PACKET) dump_sig_params = 1; bad_signature ++; rc = GPG_ERR_BAD_SIGNATURE; } /* We don't cache the result here, because we haven't completely checked that the signature is legitimate. For instance, if we have a revocation certificate on Alice's key signed by Bob, the signature may be good, but we haven't checked that Bob is a designated revoker. */ /* cache_sig_result (sig, rc); */ { int has_selfsig = 0; if (! rc && issuer == pk) { if (n2->pkt->pkttype == PKT_PUBLIC_KEY && (/* Direct key signature. */ sig->sig_class == 0x1f /* Key revocation signature. */ || sig->sig_class == 0x20)) has_selfsig = 1; if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY && (/* Subkey binding sig. */ sig->sig_class == 0x18 /* Subkey revocation sig. */ || sig->sig_class == 0x28)) has_selfsig = 1; if (n2->pkt->pkttype == PKT_USER_ID && (/* Certification sigs. */ sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13 /* Certification revocation sig. */ || sig->sig_class == 0x30)) has_selfsig = 1; } if ((n2 && n2 != last_printed_component) || (! n2 && last_printed_component != current_component)) { int is_reordered = n2 && n2 != current_component; if (n2) last_printed_component = n2; else last_printed_component = current_component; if (!modified) ; else if (last_printed_component->pkt->pkttype == PKT_USER_ID) { tty_printf ("uid "); tty_print_utf8_string (last_printed_component ->pkt->pkt.user_id->name, last_printed_component ->pkt->pkt.user_id->len); } else if (last_printed_component->pkt->pkttype == PKT_PUBLIC_KEY) tty_printf ("pub %s", pk_keyid_str (last_printed_component ->pkt->pkt.public_key)); else tty_printf ("sub %s", pk_keyid_str (last_printed_component ->pkt->pkt.public_key)); if (modified) { if (is_reordered) tty_printf (_(" (reordered signatures follow)")); tty_printf ("\n"); } } if (modified) print_one_sig (rc, kb, n, NULL, NULL, NULL, has_selfsig, 0, only_selfsigs); } if (dump_sig_params) { int i; for (i = 0; i < pubkey_get_nsig (sig->pubkey_algo); i ++) { char buffer[1024]; size_t len; char *printable; gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof (buffer), &len, sig->data[i]); printable = bin2hex (buffer, len, NULL); log_info (" %d: %s\n", i, printable); xfree (printable); } } break; default: if (DBG_PACKET) log_debug ("unhandled packet: %d\n", p->pkttype); break; } } xfree (pending_desc); pending_desc = NULL; if (issuer != pk) free_public_key (issuer); issuer = NULL; /* Identify keys / uids that don't have a self-sig. */ { int has_selfsig = 0; PACKET *p; PKT_signature *sig; current_component = NULL; for (n = kb; n; n = n->next) { if (is_deleted_kbnode (n)) continue; p = n->pkt; switch (p->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_USER_ID: if (current_component && ! has_selfsig) missing_selfsig ++; current_component = n; has_selfsig = 0; break; case PKT_SIGNATURE: if (! current_component || has_selfsig) break; sig = n->pkt->pkt.signature; if (! (sig->flags.checked && sig->flags.valid)) break; if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0) /* Different issuer, couldn't be a self-sig. */ break; if (current_component->pkt->pkttype == PKT_PUBLIC_KEY && (/* Direct key signature. */ sig->sig_class == 0x1f /* Key revocation signature. */ || sig->sig_class == 0x20)) has_selfsig = 1; if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY && (/* Subkey binding sig. */ sig->sig_class == 0x18 /* Subkey revocation sig. */ || sig->sig_class == 0x28)) has_selfsig = 1; if (current_component->pkt->pkttype == PKT_USER_ID && (/* Certification sigs. */ sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13 /* Certification revocation sig. */ || sig->sig_class == 0x30)) has_selfsig = 1; break; default: if (current_component && ! has_selfsig) missing_selfsig ++; current_component = NULL; } } } if (dups || missing_issuer || bad_signature || reordered) tty_printf (_("key %s:\n"), pk_keyid_str (pk)); if (dups) tty_printf (ngettext ("%d duplicate signature removed\n", "%d duplicate signatures removed\n", dups), dups); if (missing_issuer) tty_printf (ngettext ("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", missing_issuer), missing_issuer); if (bad_signature) tty_printf (ngettext ("%d bad signature\n", "%d bad signatures\n", bad_signature), bad_signature); if (reordered) tty_printf (ngettext ("%d signature reordered\n", "%d signatures reordered\n", reordered), reordered); if (only_selfsigs && (bad_signature || reordered)) tty_printf (_("Warning: errors found and only checked self-signatures," " run '%s' to check all signatures.\n"), "check"); return modified; } static int sign_mk_attrib (PKT_signature * sig, void *opaque) { struct sign_attrib *attrib = opaque; byte buf[8]; if (attrib->non_exportable) { buf[0] = 0; /* not exportable */ build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, buf, 1); } if (attrib->non_revocable) { buf[0] = 0; /* not revocable */ build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); } if (attrib->reason) revocation_reason_build_cb (sig, attrib->reason); if (attrib->trust_depth) { /* Not critical. If someone doesn't understand trust sigs, this can still be a valid regular signature. */ buf[0] = attrib->trust_depth; buf[1] = attrib->trust_value; build_sig_subpkt (sig, SIGSUBPKT_TRUST, buf, 2); /* Critical. If someone doesn't understands regexps, this whole sig should be invalid. Note the +1 for the length - regexps are null terminated. */ if (attrib->trust_regexp) build_sig_subpkt (sig, SIGSUBPKT_FLAG_CRITICAL | SIGSUBPKT_REGEXP, attrib->trust_regexp, strlen (attrib->trust_regexp) + 1); } return 0; } static void trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp) { char *p; *trust_value = 0; *trust_depth = 0; *regexp = NULL; /* Same string as pkclist.c:do_edit_ownertrust */ tty_printf (_ ("Please decide how far you trust this user to correctly verify" " other users' keys\n(by looking at passports, checking" " fingerprints from different sources, etc.)\n")); tty_printf ("\n"); tty_printf (_(" %d = I trust marginally\n"), 1); tty_printf (_(" %d = I trust fully\n"), 2); tty_printf ("\n"); while (*trust_value == 0) { p = cpr_get ("trustsig_prompt.trust_value", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); /* 60 and 120 are as per RFC2440 */ if (p[0] == '1' && !p[1]) *trust_value = 60; else if (p[0] == '2' && !p[1]) *trust_value = 120; xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter the depth of this trust signature.\n" "A depth greater than 1 allows the key you are" " signing to make\n" "trust signatures on your behalf.\n")); tty_printf ("\n"); while (*trust_depth == 0) { p = cpr_get ("trustsig_prompt.trust_depth", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); *trust_depth = atoi (p); xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter a domain to restrict this signature, " "or enter for none.\n")); tty_printf ("\n"); p = cpr_get ("trustsig_prompt.trust_regexp", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); if (strlen (p) > 0) { char *q = p; int regexplen = 100, ind; *regexp = xmalloc (regexplen); /* Now mangle the domain the user entered into a regexp. To do this, \-escape everything that isn't alphanumeric, and attach "<[^>]+[@.]" to the front, and ">$" to the end. */ strcpy (*regexp, "<[^>]+[@.]"); ind = strlen (*regexp); while (*q) { if (!((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9'))) (*regexp)[ind++] = '\\'; (*regexp)[ind++] = *q; if ((regexplen - ind) < 3) { regexplen += 100; *regexp = xrealloc (*regexp, regexplen); } q++; } (*regexp)[ind] = '\0'; strcat (*regexp, ">$"); } xfree (p); tty_printf ("\n"); } /* * Loop over all LOCUSR and and sign the uids after asking. If no * user id is marked, all user ids will be signed; if some user_ids * are marked only those will be signed. If QUICK is true the * function won't ask the user and use sensible defaults. */ static int sign_uids (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, strlist_t locusr, int *ret_modified, int local, int nonrevocable, int trust, int interactive, int quick) { int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; PKT_public_key *pk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk = NULL; int select_all = !count_selected_uids (keyblock) || interactive; /* Build a list of all signators. * * We use the CERT flag to request the primary which must always * be one which is capable of signing keys. I can't see a reason * why to sign keys using a subkey. Implementation of USAGE_CERT * is just a hack in getkey.c and does not mean that a subkey * marked as certification capable will be used. */ rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); if (rc) goto leave; /* Loop over all signators. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { u32 sk_keyid[2], pk_keyid[2]; char *p, *trust_regexp = NULL; int class = 0, selfsig = 0; u32 duration = 0, timestamp = 0; byte trust_depth = 0, trust_value = 0; pk = sk_rover->pk; keyid_from_pk (pk, sk_keyid); /* Set mark A for all selected user ids. */ for (node = keyblock; node; node = node->next) { if (select_all || (node->flag & NODFLG_SELUID)) node->flag |= NODFLG_MARK_A; else node->flag &= ~NODFLG_MARK_A; } /* Reset mark for uids which are already signed. */ uidnode = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { primary_pk = node->pkt->pkt.public_key; keyid_from_pk (primary_pk, pk_keyid); /* Is this a self-sig? */ if (pk_keyid[0] == sk_keyid[0] && pk_keyid[1] == sk_keyid[1]) selfsig = 1; } else if (node->pkt->pkttype == PKT_USER_ID) { uidnode = (node->flag & NODFLG_MARK_A) ? node : NULL; if (uidnode) { int yesreally = 0; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); if (opt.only_sign_text_ids && uidnode->pkt->pkt.user_id->attribs) { tty_fprintf (fp, _("Skipping user ID \"%s\"," " which is not a text ID.\n"), user); uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (uidnode->pkt->pkt.user_id->is_revoked) { tty_fprintf (fp, _("User ID \"%s\" is revoked."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.revoke_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (uidnode->pkt->pkt.user_id->is_expired) { tty_fprintf (fp, _("User ID \"%s\" is expired."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.expire_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (!uidnode->pkt->pkt.user_id->created && !selfsig) { tty_fprintf (fp, _("User ID \"%s\" is not self-signed."), user); if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.nosig_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } if (uidnode && interactive && !yesreally && !quick) { tty_fprintf (fp, _("User ID \"%s\" is signable. "), user); if (!cpr_get_answer_is_yes ("sign_uid.sign_okay", _("Sign it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } } xfree (user); } } else if (uidnode && node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class & ~3) == 0x10) { if (sk_keyid[0] == node->pkt->pkt.signature->keyid[0] && sk_keyid[1] == node->pkt->pkt.signature->keyid[1]) { char buf[50]; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); /* It's a v3 self-sig. Make it into a v4 self-sig? */ if (node->pkt->pkt.signature->version < 4 && selfsig && !quick) { tty_fprintf (fp, _("The self-signature on \"%s\"\n" "is a PGP 2.x-style signature.\n"), user); /* Note that the regular PGP2 warning below still applies if there are no v4 sigs on this key at all. */ if (opt.expert) if (cpr_get_answer_is_yes ("sign_uid.v4_promote_okay", _("Do you want to promote " "it to an OpenPGP self-" "signature? (y/N) "))) { node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Is the current signature expired? */ if (node->pkt->pkt.signature->flags.expired) { tty_fprintf (fp, _("Your current signature on \"%s\"\n" "has expired.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.replace_expired_okay", _("Do you want to issue a " "new signature to replace " "the expired one? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } if (!node->pkt->pkt.signature->flags.exportable && !local) { /* It's a local sig, and we want to make a exportable sig. */ tty_fprintf (fp, _("Your current signature on \"%s\"\n" "is a local signature.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.local_promote_okay", _("Do you want to promote " "it to a full exportable " "signature? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Fixme: see whether there is a revocation in which * case we should allow signing it again. */ if (!node->pkt->pkt.signature->flags.exportable && local) tty_fprintf ( fp, _("\"%s\" was already locally signed by key %s\n"), user, keystr_from_pk (pk)); else tty_fprintf (fp, _("\"%s\" was already signed by key %s\n"), user, keystr_from_pk (pk)); if (opt.expert && !quick && cpr_get_answer_is_yes ("sign_uid.dupe_okay", _("Do you want to sign it " "again anyway? (y/N) "))) { /* Don't delete the old sig here since this is an --expert thing. */ xfree (user); continue; } snprintf (buf, sizeof buf, "%08lX%08lX", (ulong) pk->keyid[0], (ulong) pk->keyid[1]); write_status_text (STATUS_ALREADY_SIGNED, buf); uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */ xfree (user); } } } /* Check whether any uids are left for signing. */ if (!count_uids_with_flag (keyblock, NODFLG_MARK_A)) { tty_fprintf (fp, _("Nothing to sign with key %s\n"), keystr_from_pk (pk)); continue; } /* Ask whether we really should sign these user id(s). */ tty_fprintf (fp, "\n"); show_key_with_all_names (ctrl, fp, keyblock, 1, 0, 1, 0, 0, 0); tty_fprintf (fp, "\n"); if (primary_pk->expiredate && !selfsig) { /* Static analyzer note: A claim that PRIMARY_PK might be NULL is not correct because it set from the public key packet which is always the first packet in a keyblock and parsed in the above loop over the keyblock. In case the keyblock has no packets at all and thus the loop was not entered the above count_uids_with_flag would have detected this case. */ u32 now = make_timestamp (); if (primary_pk->expiredate <= now) { tty_fprintf (fp, _("This key has expired!")); if (opt.expert && !quick) { tty_fprintf (fp, " "); if (!cpr_get_answer_is_yes ("sign_uid.expired_okay", _("Are you sure you still " "want to sign it? (y/N) "))) continue; } else { tty_fprintf (fp, _(" Unable to sign.\n")); continue; } } else { tty_fprintf (fp, _("This key is due to expire on %s.\n"), expirestr_from_pk (primary_pk)); if (opt.ask_cert_expire && !quick) { char *answer = cpr_get ("sign_uid.expire", _("Do you want your signature to " "expire at the same time? (Y/n) ")); if (answer_is_yes_no_default (answer, 1)) { /* This fixes the signature timestamp we're going to make as now. This is so the expiration date is exactly correct, and not a few seconds off (due to the time it takes to answer the questions, enter the passphrase, etc). */ timestamp = now; duration = primary_pk->expiredate - now; } cpr_kill_prompt (); xfree (answer); } } } /* Only ask for duration if we haven't already set it to match the expiration of the pk */ if (!duration && !selfsig) { if (opt.ask_cert_expire && !quick) duration = ask_expire_interval (1, opt.def_cert_expire); else duration = parse_expire_string (opt.def_cert_expire); } if (selfsig) ; else { if (opt.batch || !opt.ask_cert_level || quick) class = 0x10 + opt.def_cert_level; else { char *answer; tty_fprintf (fp, _("How carefully have you verified the key you are " "about to sign actually belongs\nto the person " "named above? If you don't know what to " "answer, enter \"0\".\n")); tty_fprintf (fp, "\n"); tty_fprintf (fp, _(" (0) I will not answer.%s\n"), opt.def_cert_level == 0 ? " (default)" : ""); tty_fprintf (fp, _(" (1) I have not checked at all.%s\n"), opt.def_cert_level == 1 ? " (default)" : ""); tty_fprintf (fp, _(" (2) I have done casual checking.%s\n"), opt.def_cert_level == 2 ? " (default)" : ""); tty_fprintf (fp, _(" (3) I have done very careful checking.%s\n"), opt.def_cert_level == 3 ? " (default)" : ""); tty_fprintf (fp, "\n"); while (class == 0) { answer = cpr_get ("sign_uid.class", _("Your selection? " "(enter '?' for more information): ")); if (answer[0] == '\0') class = 0x10 + opt.def_cert_level; /* Default */ else if (ascii_strcasecmp (answer, "0") == 0) class = 0x10; /* Generic */ else if (ascii_strcasecmp (answer, "1") == 0) class = 0x11; /* Persona */ else if (ascii_strcasecmp (answer, "2") == 0) class = 0x12; /* Casual */ else if (ascii_strcasecmp (answer, "3") == 0) class = 0x13; /* Positive */ else tty_fprintf (fp, _("Invalid selection.\n")); xfree (answer); } } if (trust && !quick) trustsig_prompt (&trust_value, &trust_depth, &trust_regexp); } if (!quick) { p = get_user_id_native (sk_keyid); tty_fprintf (fp, _("Are you sure that you want to sign this key with your\n" "key \"%s\" (%s)\n"), p, keystr_from_pk (pk)); xfree (p); } if (selfsig) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("This will be a self-signature.\n")); if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-revocable.\n")); } } else { if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-revocable.\n")); } switch (class) { case 0x11: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have not checked this key at all.\n")); break; case 0x12: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key casually.\n")); break; case 0x13: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key very carefully.\n")); break; } } tty_fprintf (fp, "\n"); if (opt.batch && opt.answer_yes) ; else if (quick) ; else if (!cpr_get_answer_is_yes ("sign_uid.okay", _("Really sign? (y/N) "))) continue; /* Now we can sign the user ids. */ reloop: /* (Must use this, because we are modifing the list.) */ primary_pk = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) primary_pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_MARK_A)) { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; log_assert (primary_pk); memset (&attrib, 0, sizeof attrib); attrib.non_exportable = local; attrib.non_revocable = nonrevocable; attrib.trust_depth = trust_depth; attrib.trust_value = trust_value; attrib.trust_regexp = trust_regexp; node->flag &= ~NODFLG_MARK_A; /* We force creation of a v4 signature for local * signatures, otherwise we would not generate the * subpacket with v3 keys and the signature becomes * exportable. */ if (selfsig) rc = make_keysig_packet (&sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, 0x13, 0, 0, 0, keygen_add_std_prefs, primary_pk, NULL); else rc = make_keysig_packet (&sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, class, 0, timestamp, duration, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto leave; } *ret_modified = 1; /* We changed the keyblock. */ update_trust = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), PKT_SIGNATURE); goto reloop; } } /* Delete any sigs that got promoted */ for (node = keyblock; node; node = node->next) if (node->flag & NODFLG_DELSIG) delete_kbnode (node); } /* End loop over signators. */ leave: release_sk_list (sk_list); return rc; } /* * Change the passphrase of the primary and all secondary keys. Note * that it is common to use only one passphrase for the primary and * all subkeys. However, this is now (since GnuPG 2.1) all up to the * gpg-agent. Returns 0 on success or an error code. */ static gpg_error_t change_passphrase (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; kbnode_t node; PKT_public_key *pk; int any; u32 keyid[2], subid[2]; char *hexgrip = NULL; char *cache_nonce = NULL; char *passwd_nonce = NULL; node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; public key missing!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); /* Check whether it is likely that we will be able to change the passphrase for any subkey. */ for (any = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *serialno; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); if (!err && serialno) ; /* Key on card. */ else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; /* Maybe stub key. */ else if (!err) any = 1; /* Key is known. */ else log_error ("key %s: error getting keyinfo from agent: %s\n", keystr_with_sub (keyid, subid), gpg_strerror (err)); xfree (serialno); } } err = 0; if (!any) { tty_printf (_("Key has only stub or on-card key items - " "no passphrase to change.\n")); goto leave; } /* Change the passphrase for all keys. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *desc; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_NORMAL, 1); err = agent_passwd (ctrl, hexgrip, desc, 0, &cache_nonce, &passwd_nonce); xfree (desc); if (err) log_log ((gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) ? GPGRT_LOG_INFO : GPGRT_LOG_ERROR, _("key %s: error changing passphrase: %s\n"), keystr_with_sub (keyid, subid), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; } } leave: xfree (hexgrip); xfree (cache_nonce); xfree (passwd_nonce); return err; } /* Fix various problems in the keyblock. Returns true if the keyblock was changed. Note that a pointer to the keyblock must be given and the function may change it (i.e. replacing the first node). */ static int fix_keyblock (kbnode_t *keyblockp) { int changed = 0; if (collapse_uids (keyblockp)) changed++; if (check_all_keysigs (*keyblockp, 0, 1)) changed++; reorder_keyblock (*keyblockp); /* If we modified the keyblock, make sure the flags are right. */ if (changed) merge_keys_and_selfsig (*keyblockp); return changed; } static int parse_sign_type (const char *str, int *localsig, int *nonrevokesig, int *trustsig) { const char *p = str; while (*p) { if (ascii_strncasecmp (p, "l", 1) == 0) { *localsig = 1; p++; } else if (ascii_strncasecmp (p, "nr", 2) == 0) { *nonrevokesig = 1; p += 2; } else if (ascii_strncasecmp (p, "t", 1) == 0) { *trustsig = 1; p++; } else return 0; } return 1; } /* * Menu driven key editor. If seckey_check is true, then a secret key * that matches username will be looked for. If it is false, not all * commands will be available. * * Note: to keep track of certain selections we use node->mark MARKBIT_xxxx. */ /* Need an SK for this command */ #define KEYEDIT_NEED_SK 1 /* Cannot be viewing the SK for this command */ #define KEYEDIT_NOT_SK 2 /* Must be viewing the SK for this command */ #define KEYEDIT_ONLY_SK 4 /* Match the tail of the string */ #define KEYEDIT_TAIL_MATCH 8 enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ cmdSHOWPREF, cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP }; static struct { const char *name; enum cmdids id; int flags; const char *desc; } cmds[] = { { "quit", cmdQUIT, 0, N_("quit this menu")}, { "q", cmdQUIT, 0, NULL}, { "save", cmdSAVE, 0, N_("save and quit")}, { "help", cmdHELP, 0, N_("show this help")}, { "?", cmdHELP, 0, NULL}, { "fpr", cmdFPR, 0, N_("show key fingerprint")}, { "grip", cmdGRIP, 0, N_("show the keygrip")}, { "list", cmdLIST, 0, N_("list key and user IDs")}, { "l", cmdLIST, 0, NULL}, { "uid", cmdSELUID, 0, N_("select user ID N")}, { "key", cmdSELKEY, 0, N_("select subkey N")}, { "check", cmdCHECK, 0, N_("check signatures")}, { "c", cmdCHECK, 0, NULL}, { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH, N_("sign selected user IDs [* see below for related commands]")}, { "s", cmdSIGN, KEYEDIT_NOT_SK, NULL}, /* "lsign" and friends will never match since "sign" comes first and it is a tail match. They are just here so they show up in the help menu. */ { "lsign", cmdNOP, 0, N_("sign selected user IDs locally")}, { "tsign", cmdNOP, 0, N_("sign selected user IDs with a trust signature")}, { "nrsign", cmdNOP, 0, N_("sign selected user IDs with a non-revocable signature")}, { "debug", cmdDEBUG, 0, NULL}, { "adduid", cmdADDUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a user ID")}, { "addphoto", cmdADDPHOTO, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a photo ID")}, { "deluid", cmdDELUID, KEYEDIT_NOT_SK, N_("delete selected user IDs")}, /* delphoto is really deluid in disguise */ { "delphoto", cmdDELUID, KEYEDIT_NOT_SK, NULL}, { "addkey", cmdADDKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a subkey")}, #ifdef ENABLE_CARD_SUPPORT { "addcardkey", cmdADDCARDKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a key to a smartcard")}, { "keytocard", cmdKEYTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, N_("move a key to a smartcard")}, { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, N_("move a backup key to a smartcard")}, #endif /*ENABLE_CARD_SUPPORT */ { "delkey", cmdDELKEY, KEYEDIT_NOT_SK, N_("delete selected subkeys")}, { "addrevoker", cmdADDREVOKER, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a revocation key")}, { "delsig", cmdDELSIG, KEYEDIT_NOT_SK, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("change the expiration date for the key or selected subkeys")}, { "primary", cmdPRIMARY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("flag the selected user ID as primary")}, { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, /* Dummy command. */ { "t", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, { "pref", cmdPREF, KEYEDIT_NOT_SK, N_("list preferences (expert)")}, { "showpref", cmdSHOWPREF, KEYEDIT_NOT_SK, N_("list preferences (verbose)")}, { "setpref", cmdSETPREF, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set preference list for the selected user IDs")}, { "updpref", cmdSETPREF, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "keyserver", cmdPREFKS, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set the preferred keyserver URL for the selected user IDs")}, { "notation", cmdNOTATION, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set a notation for the selected user IDs")}, { "passwd", cmdPASSWD, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("change the passphrase")}, { "password", cmdPASSWD, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, #ifndef NO_TRUST_MODELS { "trust", cmdTRUST, KEYEDIT_NOT_SK, N_("change the ownertrust")}, #endif /*!NO_TRUST_MODELS*/ { "revsig", cmdREVSIG, KEYEDIT_NOT_SK, N_("revoke signatures on the selected user IDs")}, { "revuid", cmdREVUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("revoke selected user IDs")}, { "revphoto", cmdREVUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "revkey", cmdREVKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("revoke key or selected subkeys")}, #ifndef NO_TRUST_MODELS { "enable", cmdENABLEKEY, KEYEDIT_NOT_SK, N_("enable key")}, { "disable", cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key")}, #endif /*!NO_TRUST_MODELS*/ { "showphoto", cmdSHOWPHOTO, 0, N_("show selected photo IDs")}, { "clean", cmdCLEAN, KEYEDIT_NOT_SK, N_("compact unusable user IDs and remove unusable signatures from key")}, { "minimize", cmdMINIMIZE, KEYEDIT_NOT_SK, N_("compact unusable user IDs and remove all signatures from key")}, { NULL, cmdNONE, 0, NULL} }; #ifdef HAVE_LIBREADLINE /* These two functions are used by readline for command completion. */ static char * command_generator (const char *text, int state) { static int list_index, len; const char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next partial match */ while ((name = cmds[list_index].name)) { /* Only complete commands that have help text */ if (cmds[list_index++].desc && strncmp (name, text, len) == 0) return strdup (name); } return NULL; } static char ** keyedit_completion (const char *text, int start, int end) { /* If we are at the start of a line, we try and command-complete. If not, just do nothing for now. */ (void) end; if (start == 0) return rl_completion_matches (text, command_generator); rl_attempted_completion_over = 1; return NULL; } #endif /* HAVE_LIBREADLINE */ /* Main function of the menu driven key editor. */ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check) { enum cmdids cmd = 0; gpg_error_t err = 0; KBNODE keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int have_seckey = 0; char *answer = NULL; int redisplay = 1; int modified = 0; int sec_shadowing = 0; int run_subkey_warnings = 0; int have_commands = !!commands; if (opt.command_fd != -1) ; else if (opt.batch && !have_commands) { log_error (_("can't do this in batch mode\n")); goto leave; } #ifdef HAVE_W32_SYSTEM /* Due to Windows peculiarities we need to make sure that the trustdb stale check is done before we open another file (i.e. by searching for a key). In theory we could make sure that the files are closed after use but the open/close caches inhibits that and flushing the cache right before the stale check is not easy to implement. Thus we take the easy way out and run the stale check as early as possible. Note, that for non- W32 platforms it is run indirectly trough a call to get_validity (). */ check_trustdb_stale (ctrl); #endif /* Get the public key */ err = get_pubkey_byname (ctrl, NULL, NULL, username, &keyblock, &kdbhd, 1, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } if (fix_keyblock (&keyblock)) modified++; /* See whether we have a matching secret key. */ if (seckey_check) { have_seckey = !agent_probe_any_secret_key (ctrl, keyblock); if (have_seckey && !quiet) tty_printf (_("Secret key is available.\n")); } /* Main command loop. */ for (;;) { int i, arg_number, photo; const char *arg_string = ""; char *p; PKT_public_key *pk = keyblock->pkt->pkt.public_key; tty_printf ("\n"); if (redisplay && !quiet) { /* Show using flags: with_revoker, with_subkeys. */ show_key_with_all_names (ctrl, NULL, keyblock, 0, 1, 0, 1, 0, 0); tty_printf ("\n"); redisplay = 0; } if (run_subkey_warnings) { run_subkey_warnings = 0; if (!count_selected_keys (keyblock)) subkey_expire_warning (keyblock); } do { xfree (answer); if (have_commands) { if (commands) { answer = xstrdup (commands->d); commands = commands->next; } else if (opt.batch) { answer = xstrdup ("quit"); } else have_commands = 0; } if (!have_commands) { #ifdef HAVE_LIBREADLINE tty_enable_completion (keyedit_completion); #endif answer = cpr_get_no_help ("keyedit.prompt", GPG_NAME "> "); cpr_kill_prompt (); tty_disable_completion (); } trim_spaces (answer); } while (*answer == '#'); arg_number = 0; /* Here is the init which egcc complains about. */ photo = 0; /* Same here. */ if (!*answer) cmd = cmdLIST; else if (*answer == CONTROL_D) cmd = cmdQUIT; else if (digitp (answer)) { cmd = cmdSELUID; arg_number = atoi (answer); } else { if ((p = strchr (answer, ' '))) { *p++ = 0; trim_spaces (answer); trim_spaces (p); arg_number = atoi (p); arg_string = p; } for (i = 0; cmds[i].name; i++) { if (cmds[i].flags & KEYEDIT_TAIL_MATCH) { size_t l = strlen (cmds[i].name); size_t a = strlen (answer); if (a >= l) { if (!ascii_strcasecmp (&answer[a - l], cmds[i].name)) { answer[a - l] = '\0'; break; } } } else if (!ascii_strcasecmp (answer, cmds[i].name)) break; } if ((cmds[i].flags & KEYEDIT_NEED_SK) && !have_seckey) { tty_printf (_("Need the secret key to do this.\n")); cmd = cmdNOP; } else cmd = cmds[i].id; } /* Dispatch the command. */ switch (cmd) { case cmdHELP: for (i = 0; cmds[i].name; i++) { if ((cmds[i].flags & KEYEDIT_NEED_SK) && !have_seckey) ; /* Skip those item if we do not have the secret key. */ else if (cmds[i].desc) tty_printf ("%-11s %s\n", cmds[i].name, _(cmds[i].desc)); } tty_printf ("\n"); tty_printf (_("* The 'sign' command may be prefixed with an 'l' for local " "signatures (lsign),\n" " a 't' for trust signatures (tsign), an 'nr' for " "non-revocable signatures\n" " (nrsign), or any combination thereof (ltsign, " "tnrsign, etc.).\n")); break; case cmdLIST: redisplay = 1; break; case cmdFPR: show_key_and_fingerprint (keyblock, (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1)))); break; case cmdGRIP: show_key_and_grip (keyblock); break; case cmdSELUID: if (strlen (arg_string) == NAMEHASH_LEN * 2) redisplay = menu_select_uid_namehash (keyblock, arg_string); else { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ redisplay = menu_select_uid (keyblock, arg_number); } break; case cmdSELKEY: { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ if (menu_select_key (keyblock, arg_number, p)) redisplay = 1; } break; case cmdCHECK: if (check_all_keysigs (keyblock, count_selected_uids (keyblock), !strcmp (arg_string, "selfsig"))) modified = 1; break; case cmdSIGN: { int localsig = 0, nonrevokesig = 0, trustsig = 0, interactive = 0; if (pk->flags.revoked) { tty_printf (_("Key is revoked.")); if (opt.expert) { tty_printf (" "); if (!cpr_get_answer_is_yes ("keyedit.sign_revoked.okay", _("Are you sure you still want to sign it? (y/N) "))) break; } else { tty_printf (_(" Unable to sign.\n")); break; } } if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock)) { int result; if (opt.only_sign_text_ids) result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all user IDs? (y/N) ")); else result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all text user IDs? (y/N) ")); if (! result) { if (opt.interactive) interactive = 1; else { tty_printf (_("Hint: Select the user IDs to sign\n")); have_commands = 0; break; } } } /* What sort of signing are we doing? */ if (!parse_sign_type (answer, &localsig, &nonrevokesig, &trustsig)) { tty_printf (_("Unknown signature type '%s'\n"), answer); break; } sign_uids (ctrl, NULL, keyblock, locusr, &modified, localsig, nonrevokesig, trustsig, interactive, 0); } break; case cmdDEBUG: dump_kbnode (keyblock); break; case cmdTOGGLE: /* The toggle command is a leftover from old gpg versions where we worked with a secret and a public keyring. It is not necessary anymore but we keep this command for the sake of scripts using it. */ redisplay = 1; break; case cmdADDPHOTO: if (RFC2440) { tty_printf (_("This command is not allowed while in %s mode.\n"), compliance_option_string ()); break; } photo = 1; /* fall through */ case cmdADDUID: if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL)) { update_trust = 1; redisplay = 1; modified = 1; merge_keys_and_selfsig (keyblock); } break; case cmdDELUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (real_uids_left (keyblock) < 1) tty_printf (_("You can't delete the last user ID!\n")); else if (cpr_get_answer_is_yes ("keyedit.remove.uid.okay", n1 > 1 ? _("Really remove all selected user IDs? (y/N) ") : _("Really remove this user ID? (y/N) "))) { menu_deluid (keyblock); redisplay = 1; modified = 1; } } break; case cmdDELSIG: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (menu_delsig (keyblock)) { /* No redisplay here, because it may scroll away some * of the status output of this command. */ modified = 1; } } break; case cmdADDKEY: if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (keyblock); } break; #ifdef ENABLE_CARD_SUPPORT case cmdADDCARDKEY: if (!card_generate_subkey (keyblock)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (keyblock); } break; case cmdKEYTOCARD: { KBNODE node = NULL; switch (count_selected_keys (keyblock)) { case 0: if (cpr_get_answer_is_yes ("keyedit.keytocard.use_primary", /* TRANSLATORS: Please take care: This is about moving the key and not about removing it. */ _("Really move the primary key? (y/N) "))) node = keyblock; break; case 1: for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && node->flag & NODFLG_SELKEY) break; } break; default: tty_printf (_("You must select exactly one key.\n")); break; } if (node) { PKT_public_key *xxpk = node->pkt->pkt.public_key; if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0)) { redisplay = 1; sec_shadowing = 1; } } } break; case cmdBKUPTOCARD: { /* Ask for a filename, check whether this is really a backup key as generated by the card generation, parse that key and store it on card. */ KBNODE node; char *fname; PACKET *pkt; IOBUF a; if (!*arg_string) { tty_printf (_("Command expects a filename argument\n")); break; } if (*arg_string == DIRSEP_C) fname = xstrdup (arg_string); else if (*arg_string == '~') fname = make_filename (arg_string, NULL); else fname = make_filename (gnupg_homedir (), arg_string, NULL); /* Open that file. */ a = iobuf_open (fname); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if (!a) { tty_printf (_("Can't open '%s': %s\n"), fname, strerror (errno)); xfree (fname); break; } /* Parse and check that file. */ pkt = xmalloc (sizeof *pkt); init_packet (pkt); err = parse_packet (a, pkt); iobuf_close (a); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname); if (!err && pkt->pkttype != PKT_SECRET_KEY && pkt->pkttype != PKT_SECRET_SUBKEY) err = GPG_ERR_NO_SECKEY; if (err) { tty_printf (_("Error reading backup key from '%s': %s\n"), fname, gpg_strerror (err)); xfree (fname); free_packet (pkt); xfree (pkt); break; } xfree (fname); node = new_kbnode (pkt); /* Transfer it to gpg-agent which handles secret keys. */ err = transfer_secret_keys (ctrl, NULL, node, 1, 1); /* Treat the pkt as a public key. */ pkt->pkttype = PKT_PUBLIC_KEY; /* Ask gpg-agent to store the secret key to card. */ if (card_store_subkey (node, 0)) { redisplay = 1; sec_shadowing = 1; } release_kbnode (node); } break; #endif /* ENABLE_CARD_SUPPORT */ case cmdDELKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { tty_printf (_("You must select at least one key.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "key"); } else if (!cpr_get_answer_is_yes ("keyedit.remove.subkey.okay", n1 > 1 ? _("Do you really want to delete the " "selected keys? (y/N) ") : _("Do you really want to delete this key? (y/N) "))) ; else { menu_delkey (keyblock); redisplay = 1; modified = 1; } } break; case cmdADDREVOKER: { int sensitive = 0; if (ascii_strcasecmp (arg_string, "sensitive") == 0) sensitive = 1; if (menu_addrevoker (ctrl, keyblock, sensitive)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (keyblock); } } break; case cmdREVUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (cpr_get_answer_is_yes ("keyedit.revoke.uid.okay", n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ") : _("Really revoke this user ID? (y/N) "))) { if (menu_revuid (ctrl, keyblock)) { modified = 1; redisplay = 1; } } } break; case cmdREVKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", _("Do you really want to revoke" " the entire key? (y/N) "))) { if (menu_revkey (keyblock)) modified = 1; redisplay = 1; } } else if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", n1 > 1 ? _("Do you really want to revoke" " the selected subkeys? (y/N) ") : _("Do you really want to revoke" " this subkey? (y/N) "))) { if (menu_revsubkey (keyblock)) modified = 1; redisplay = 1; } if (modified) merge_keys_and_selfsig (keyblock); } break; case cmdEXPIRE: if (menu_expire (keyblock)) { merge_keys_and_selfsig (keyblock); run_subkey_warnings = 1; modified = 1; redisplay = 1; } break; case cmdCHANGEUSAGE: if (menu_changeusage (keyblock)) { merge_keys_and_selfsig (keyblock); modified = 1; redisplay = 1; } break; case cmdBACKSIGN: if (menu_backsign (keyblock)) { modified = 1; redisplay = 1; } break; case cmdPRIMARY: if (menu_set_primary_uid (keyblock)) { merge_keys_and_selfsig (keyblock); modified = 1; redisplay = 1; } break; case cmdPASSWD: change_passphrase (ctrl, keyblock); break; #ifndef NO_TRUST_MODELS case cmdTRUST: if (opt.trust_model == TM_EXTERNAL) { tty_printf (_("Owner trust may not be set while " "using a user provided trust database\n")); break; } show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0); tty_printf ("\n"); if (edit_ownertrust (ctrl, find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt. public_key, 1)) { redisplay = 1; /* No real need to set update_trust here as edit_ownertrust() calls revalidation_mark() anyway. */ update_trust = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 1); } break; case cmdSHOWPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 2); } break; case cmdSETPREF: { PKT_user_id *tempuid; keygen_set_std_prefs (!*arg_string ? "default" : arg_string, 0); tempuid = keygen_get_std_prefs (); tty_printf (_("Set preference list to:\n")); show_prefs (tempuid, NULL, 1); free_user_id (tempuid); if (cpr_get_answer_is_yes ("keyedit.setpref.okay", count_selected_uids (keyblock) ? _("Really update the preferences" " for the selected user IDs? (y/N) ") : _("Really update the preferences? (y/N) "))) { if (menu_set_preferences (keyblock)) { merge_keys_and_selfsig (keyblock); modified = 1; redisplay = 1; } } } break; case cmdPREFKS: if (menu_set_keyserver_url (*arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (keyblock); modified = 1; redisplay = 1; } break; case cmdNOTATION: if (menu_set_notation (*arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (keyblock); modified = 1; redisplay = 1; } break; case cmdNOP: break; case cmdREVSIG: if (menu_revsig (keyblock)) { redisplay = 1; modified = 1; } break; #ifndef NO_TRUST_MODELS case cmdENABLEKEY: case cmdDISABLEKEY: if (enable_disable_key (keyblock, cmd == cmdDISABLEKEY)) { redisplay = 1; modified = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdSHOWPHOTO: menu_showphoto (ctrl, keyblock); break; case cmdCLEAN: if (menu_clean (keyblock, 0)) redisplay = modified = 1; break; case cmdMINIMIZE: if (menu_clean (keyblock, 1)) redisplay = modified = 1; break; case cmdQUIT: if (have_commands) goto leave; if (!modified && !sec_shadowing) goto leave; if (!cpr_get_answer_is_yes ("keyedit.save.okay", _("Save changes? (y/N) "))) { if (cpr_enabled () || cpr_get_answer_is_yes ("keyedit.cancel.okay", _("Quit without saving? (y/N) "))) goto leave; break; } /* fall through */ case cmdSAVE: if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (sec_shadowing) { err = agent_scd_learn (NULL, 1); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (!modified && !sec_shadowing) tty_printf (_("Key not changed so no update needed.\n")); if (update_trust) { revalidation_mark (); update_trust = 0; } goto leave; case cmdINVCMD: default: tty_printf ("\n"); tty_printf (_("Invalid command (try \"help\")\n")); break; } } /* End of the main command loop. */ leave: release_kbnode (keyblock); keydb_release (kdbhd); xfree (answer); } /* Change the passphrase of the secret key identified by USERNAME. */ void keyedit_passwd (ctrl_t ctrl, const char *username) { gpg_error_t err; PKT_public_key *pk; kbnode_t keyblock = NULL; pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); goto leave; } err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock); if (err) goto leave; err = change_passphrase (ctrl, keyblock); leave: release_kbnode (keyblock); free_public_key (pk); if (err) { log_info ("error changing the passphrase for '%s': %s\n", username, gpg_strerror (err)); write_status_error ("keyedit.passwd", err); } else write_status_text (STATUS_SUCCESS, "keyedit.passwd"); } /* Unattended adding of a new keyid. USERNAME specifies the key. NEWUID is the new user id to add to the key. */ void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; KEYDB_SEARCH_DESC desc; kbnode_t keyblock = NULL; kbnode_t node; char *uidstring = NULL; uidstring = xstrdup (newuid); trim_spaces (uidstring); if (!*uidstring) { log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ kdbhd = keydb_new (); if (!kdbhd) { /* Note that keydb_new has already used log_error. */ goto leave; } err = classify_user_id (username, &desc, 1); if (!err) err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) { err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } /* Now with the keyblock retrieved, search again to detect an ambiguous specification. We need to save the found state so that we can do an update later. */ keydb_push_found_state (kdbhd); err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; keydb_pop_found_state (kdbhd); if (!err) { /* We require the secret primary key to add a UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) BUG (); err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key); } } if (err) { log_error (_("secret key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } fix_keyblock (&keyblock); merge_keys_and_selfsig (keyblock); if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring)) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (); } leave: xfree (uidstring); release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended revocation of a keyid. USERNAME specifies the key. UIDTOREV is the user id revoke from the key. */ void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; KEYDB_SEARCH_DESC desc; kbnode_t keyblock = NULL; kbnode_t node; int modified = 0; size_t revlen; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ kdbhd = keydb_new (); if (!kdbhd) { /* Note that keydb_new has already used log_error. */ goto leave; } err = classify_user_id (username, &desc, 1); if (!err) err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) { err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } /* Now with the keyblock retrieved, search again to detect an ambiguous specification. We need to save the found state so that we can do an update later. */ keydb_push_found_state (kdbhd); err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; keydb_pop_found_state (kdbhd); if (!err) { /* We require the secret primary key to revoke a UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) BUG (); err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key); } } if (err) { log_error (_("secret key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } fix_keyblock (&keyblock); setup_main_keyids (keyblock); revlen = strlen (uidtorev); /* find the right UID */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && revlen == node->pkt->pkt.user_id->len && !memcmp (node->pkt->pkt.user_id->name, uidtorev, revlen)) { struct revocation_reason_info *reason; reason = get_default_uid_revocation_reason (); err = core_revuid (ctrl, keyblock, node, reason, &modified); release_revocation_reason_info (reason); if (err) { log_error (_("User ID revocation failed: %s\n"), gpg_strerror (err)); goto leave; } err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (); goto leave; } } leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Find a keyblock by fingerprint because only this uniquely * identifies a key and may thus be used to select a key for * unattended subkey creation os key signing. */ static gpg_error_t find_by_primary_fpr (ctrl_t ctrl, const char *fpr, kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; KEYDB_SEARCH_DESC desc; byte fprbin[MAX_FINGERPRINT_LEN]; size_t fprlen; *r_keyblock = NULL; *r_kdbhd = NULL; if (classify_user_id (fpr, &desc, 1) || !(desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_FPR16 || desc.mode == KEYDB_SEARCH_MODE_FPR20)) { log_error (_("\"%s\" is not a fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); goto leave; } /* Check that the primary fingerprint has been given. */ fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen); if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16 && !memcmp (fprbin, desc.u.fpr, 16)) ; else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR && !memcmp (fprbin, desc.u.fpr, 16) && !desc.u.fpr[16] && !desc.u.fpr[17] && !desc.u.fpr[18] && !desc.u.fpr[19]) ; else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20 || desc.mode == KEYDB_SEARCH_MODE_FPR) && !memcmp (fprbin, desc.u.fpr, 20)) ; else { log_error (_("\"%s\" is not the primary fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } *r_keyblock = keyblock; keyblock = NULL; *r_kdbhd = kdbhd; kdbhd = NULL; err = 0; leave: release_kbnode (keyblock); keydb_release (kdbhd); return err; } /* Unattended key signing function. If the key specifified by FPR is available and FPR is the primary fingerprint all user ids of the key are signed using the default signing key. If UIDS is an empty list all usable UIDs are signed, if it is not empty, only those user ids matching one of the entries of the list are signed. With LOCAL being true the signatures are marked as non-exportable. */ void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int modified = 0; PKT_public_key *pk; kbnode_t node; strlist_t sl; int any; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a key and may thus be used to select a key for unattended key signing. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (&keyblock)) modified++; /* Give some info in verbose. */ if (opt.verbose) { show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 1/*with_revoker*/, 1/*with_fingerprint*/, 0, 0, 1); es_fflush (es_stdout); } pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), _(" Unable to sign.\n")); goto leave; } /* Set the flags according to the UIDS list. Fixme: We may want to use classify_user_id along with dedicated compare functions so that we match the same way as in the key lookup. */ any = 0; menu_select_uid (keyblock, 0); /* Better clear the flags first. */ for (sl=uids; sl; sl = sl->next) { const char *name = sl->d; int count = 0; sl->flags &= ~(1|2); /* Clear flags used for error reporting. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->attrib_data) ; else if (*name == '=' && strlen (name+1) == uid->len && !memcmp (uid->name, name + 1, uid->len)) { /* Exact match - we don't do a check for ambiguity * in this case. */ node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } } else if (ascii_memistr (uid->name, uid->len, *name == '*'? name+1:name)) { node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } count++; } } } if (count > 1) { any = -1; /* Force failure at end. */ sl->flags |= 2; /* Report as ambiguous. */ } } /* Check whether all given user ids were found. */ for (sl=uids; sl; sl = sl->next) if (!(sl->flags & 1)) any = -1; /* That user id was not found. */ /* Print an error if there was a problem with the user ids. */ if (uids && any < 1) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); es_fflush (es_stdout); for (sl=uids; sl; sl = sl->next) { if ((sl->flags & 2)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_AMBIGUOUS_NAME)); else if (!(sl->flags & 1)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_NOT_FOUND)); } log_error ("%s %s", _("No matching user IDs."), _("Nothing to sign.\n")); goto leave; } /* Sign. */ sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1); es_fflush (es_stdout); if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); if (update_trust) revalidation_mark (); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended subkey creation function. * */ void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err; kbnode_t keyblock; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a * key and may thus be used to select a key for unattended subkey * creation. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (&keyblock)) modified++; pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), "\n"); goto leave; } /* Create the subkey. Note that the called function already prints * an error message. */ if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr)) modified = 1; es_fflush (es_stdout); /* Store. */ if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); } static void tty_print_notations (int indent, PKT_signature * sig) { int first = 1; struct notation *notation, *nd; if (indent < 0) { first = 0; indent = -indent; } notation = sig_to_notation (sig); for (nd = notation; nd; nd = nd->next) { if (!first) tty_printf ("%*s", indent, ""); else first = 0; tty_print_utf8_string (nd->name, strlen (nd->name)); tty_printf ("="); tty_print_utf8_string (nd->value, strlen (nd->value)); tty_printf ("\n"); } free_notation (notation); } /* * Show preferences of a public keyblock. */ static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) { const prefitem_t fake = { 0, 0 }; const prefitem_t *prefs; int i; if (!uid) return; if (uid->prefs) prefs = uid->prefs; else if (verbose) prefs = &fake; else return; if (verbose) { int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; tty_printf (" "); tty_printf (_("Cipher: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_SYM) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!openpgp_cipher_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", openpgp_cipher_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == CIPHER_ALGO_3DES) des_seen = 1; } } if (!des_seen) { if (any) tty_printf (", "); tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); } tty_printf ("\n "); tty_printf (_("Digest: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_HASH) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", gcry_md_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == DIGEST_ALGO_SHA1) sha1_seen = 1; } } if (!sha1_seen) { if (any) tty_printf (", "); tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); } tty_printf ("\n "); tty_printf (_("Compression: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_ZIP) { const char *s = compress_algo_to_string (prefs[i].value); if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (s && prefs[i].value < 100) tty_printf ("%s", s); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == COMPRESS_ALGO_NONE) uncomp_seen = 1; } } if (!uncomp_seen) { if (any) tty_printf (", "); else { tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_ZIP)); tty_printf (", "); } tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); } if (uid->flags.mdc || !uid->flags.ks_modify) { tty_printf ("\n "); tty_printf (_("Features: ")); any = 0; if (uid->flags.mdc) { tty_printf ("MDC"); any = 1; } if (!uid->flags.ks_modify) { if (any) tty_printf (", "); tty_printf (_("Keyserver no-modify")); } } tty_printf ("\n"); if (selfsig) { const byte *pref_ks; size_t pref_ks_len; pref_ks = parse_sig_subpkt (selfsig->hashed, SIGSUBPKT_PREF_KS, &pref_ks_len); if (pref_ks && pref_ks_len) { tty_printf (" "); tty_printf (_("Preferred keyserver: ")); tty_print_utf8_string (pref_ks, pref_ks_len); tty_printf ("\n"); } if (selfsig->flags.notation) { tty_printf (" "); tty_printf (_("Notations: ")); tty_print_notations (5 + strlen (_("Notations: ")), selfsig); } } } else { tty_printf (" "); for (i = 0; prefs[i].type; i++) { tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : prefs[i].type == PREFTYPE_HASH ? 'H' : prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[i].value); } if (uid->flags.mdc) tty_printf (" [mdc]"); if (!uid->flags.ks_modify) tty_printf (" [no-ks-modify]"); tty_printf ("\n"); } } /* This is the version of show_key_with_all_names used when opt.with_colons is used. It prints all available data in a easy to parse format and does not translate utf8 */ static void show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) { KBNODE node; int i, j, ulti_hack = 0; byte pk_version = 0; PKT_public_key *primary = NULL; int have_seckey; if (!fp) fp = es_stdout; /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) { PKT_public_key *pk = node->pkt->pkt.public_key; u32 keyid[2]; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk_version = pk->version; primary = pk; } keyid_from_pk (pk, keyid); have_seckey = !agent_probe_secret_key (ctrl, pk); if (node->pkt->pkttype == PKT_PUBLIC_KEY) es_fputs (have_seckey? "sec:" : "pub:", fp); else es_fputs (have_seckey? "ssb:" : "sub:", fp); if (!pk->flags.valid) es_putc ('i', fp); else if (pk->flags.revoked) es_putc ('r', fp); else if (pk->has_expired) es_putc ('e', fp); else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks)) { - int trust = get_validity_info (ctrl, pk, NULL); + int trust = get_validity_info (ctrl, keyblock, pk, NULL); if (trust == 'u') ulti_hack = 1; es_putc (trust, fp); } es_fprintf (fp, ":%u:%d:%08lX%08lX:%lu:%lu::", nbits_from_pk (pk), pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], (ulong) pk->timestamp, (ulong) pk->expiredate); if (node->pkt->pkttype == PKT_PUBLIC_KEY && !(opt.fast_list_mode || opt.no_expensive_trust_checks)) es_putc (get_ownertrust_info (pk), fp); es_putc (':', fp); es_putc (':', fp); es_putc (':', fp); /* Print capabilities. */ if ((pk->pubkey_usage & PUBKEY_USAGE_ENC)) es_putc ('e', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_SIG)) es_putc ('s', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_CERT)) es_putc ('c', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) es_putc ('a', fp); es_putc ('\n', fp); print_fingerprint (fp, pk, 0); print_revokers (fp, pk); } } /* the user ids */ i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (uid->attrib_data) es_fputs ("uat:", fp); else es_fputs ("uid:", fp); if (uid->is_revoked) es_fputs ("r::::::::", fp); else if (uid->is_expired) es_fputs ("e::::::::", fp); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) es_fputs ("::::::::", fp); else { int uid_validity; if (primary && !ulti_hack) - uid_validity = get_validity_info (ctrl, primary, uid); + uid_validity = get_validity_info (ctrl, keyblock, primary, uid); else uid_validity = 'u'; es_fprintf (fp, "%c::::::::", uid_validity); } if (uid->attrib_data) es_fprintf (fp, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (fp, uid->name, uid->len, ":", NULL); es_putc (':', fp); /* signature class */ es_putc (':', fp); /* capabilities */ es_putc (':', fp); /* preferences */ if (pk_version > 3 || uid->selfsigversion > 3) { const prefitem_t *prefs = uid->prefs; for (j = 0; prefs && prefs[j].type; j++) { if (j) es_putc (' ', fp); es_fprintf (fp, "%c%d", prefs[j].type == PREFTYPE_SYM ? 'S' : prefs[j].type == PREFTYPE_HASH ? 'H' : prefs[j].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[j].value); } if (uid->flags.mdc) es_fputs (",mdc", fp); if (!uid->flags.ks_modify) es_fputs (",no-ks-modify", fp); } es_putc (':', fp); /* flags */ es_fprintf (fp, "%d,", i); if (uid->is_primary) es_putc ('p', fp); if (uid->is_revoked) es_putc ('r', fp); if (uid->is_expired) es_putc ('e', fp); if ((node->flag & NODFLG_SELUID)) es_putc ('s', fp); if ((node->flag & NODFLG_MARK_A)) es_putc ('m', fp); es_putc (':', fp); if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { #ifdef USE_TOFU enum tofu_policy policy; if (! tofu_get_policy (ctrl, primary, uid, &policy) && policy != TOFU_POLICY_NONE) es_fprintf (fp, "%s", tofu_policy_str (policy)); #endif /*USE_TOFU*/ } es_putc (':', fp); es_putc ('\n', fp); } } } static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node)) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (!flag || (flag && (node->flag & flag))) { if (!(flag & NODFLG_MARK_A) && pk) tty_fprintf (fp, "%s ", uid_trust_string_fixed (ctrl, pk, uid)); if (flag & NODFLG_MARK_A) tty_fprintf (fp, " "); else if (node->flag & NODFLG_SELUID) tty_fprintf (fp, "(%d)* ", i); else if (uid->is_primary) tty_fprintf (fp, "(%d). ", i); else tty_fprintf (fp, "(%d) ", i); tty_print_utf8_string2 (fp, uid->name, uid->len, 0); tty_fprintf (fp, "\n"); if (with_prefs && pk) { if (pk->version > 3 || uid->selfsigversion > 3) { PKT_signature *selfsig = NULL; KBNODE signode; for (signode = node->next; signode && signode->pkt->pkttype == PKT_SIGNATURE; signode = signode->next) { if (signode->pkt->pkt.signature-> flags.chosen_selfsig) { selfsig = signode->pkt->pkt.signature; break; } } show_prefs (uid, selfsig, with_prefs == 2); } else tty_fprintf (fp, _("There are no preferences on a" " PGP 2.x-style user ID.\n")); } } } } } /* * Display the key a the user ids, if only_marked is true, do only so * for user ids with mark A flag set and do not display the index * number. If FP is not NULL print to the given stream and not to the * tty (ignored in with-colons mode). */ static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn) { gpg_error_t err; kbnode_t node; int i; int do_warn = 0; int have_seckey = 0; char *serialno = NULL; PKT_public_key *primary = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; if (opt.with_colons) { show_key_with_all_names_colon (ctrl, fp, keyblock); return; } /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY && !is_deleted_kbnode (node))) { PKT_public_key *pk = node->pkt->pkt.public_key; const char *otrust = "err"; const char *trust = "err"; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { /* do it here, so that debug messages don't clutter the * output */ static int did_warn = 0; trust = get_validity_string (ctrl, pk, NULL); otrust = get_ownertrust_string (pk); /* Show a warning once */ if (!did_warn - && (get_validity (ctrl, pk, NULL, NULL, 0) + && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0) & TRUST_FLAG_PENDING_CHECK)) { did_warn = 1; do_warn = 1; } primary = pk; } if (pk->flags.revoked) { char *user = get_user_id_string_native (pk->revoked.keyid); tty_fprintf (fp, _("The following key was revoked on" " %s by %s key %s\n"), revokestr_from_pk (pk), gcry_pk_algo_name (pk->revoked.algo), user); xfree (user); } if (with_revoker) { if (!pk->revkey && pk->numrevkeys) BUG (); else for (i = 0; i < pk->numrevkeys; i++) { u32 r_keyid[2]; char *user; const char *algo; algo = gcry_pk_algo_name (pk->revkey[i].algid); keyid_from_fingerprint (pk->revkey[i].fpr, MAX_FINGERPRINT_LEN, r_keyid); user = get_user_id_string_native (r_keyid); tty_fprintf (fp, _("This key may be revoked by %s key %s"), algo ? algo : "?", user); if (pk->revkey[i].class & 0x40) { tty_fprintf (fp, " "); tty_fprintf (fp, _("(sensitive)")); } tty_fprintf (fp, "\n"); xfree (user); } } keyid_from_pk (pk, NULL); xfree (serialno); serialno = NULL; { char *hexgrip; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("error computing a keygrip: %s\n", gpg_strerror (err)); have_seckey = 0; } else have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); xfree (hexgrip); } tty_fprintf (fp, "%s%c %s/%s", node->pkt->pkttype == PKT_PUBLIC_KEY && have_seckey? "sec" : node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" : have_seckey ? "ssb" : "sub", (node->flag & NODFLG_SELKEY) ? '*' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (pk->keyid)); if (opt.legacy_list_mode) tty_fprintf (fp, " "); else tty_fprintf (fp, "\n "); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); if (pk->flags.revoked) tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); else if (pk->has_expired) tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); else tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("usage: %s"), usagestr_from_pk (pk, 1)); tty_fprintf (fp, "\n"); if (serialno) { /* The agent told us that a secret key is available and that it has been stored on a card. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s\n", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s\n", serialno); } else if (pk->seckey_info && pk->seckey_info->is_protected && pk->seckey_info->s2k.mode == 1002) { /* FIXME: Check wether this code path is still used. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (pk->seckey_info->ivlen == 16 && !memcmp (pk->seckey_info->iv, "\xD2\x76\x00\x01\x24\x01", 6)) { /* This is an OpenPGP card. */ for (i = 8; i < 14; i++) { if (i == 10) tty_fprintf (fp, " "); tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } } else { /* Unknown card: Print all. */ for (i = 0; i < pk->seckey_info->ivlen; i++) tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { if (opt.trust_model != TM_ALWAYS) { tty_fprintf (fp, "%*s", opt.legacy_list_mode? ((int) keystrlen () + 13):5, ""); /* Ownertrust is only meaningful for the PGP or classic trust models, or PGP combined with TOFU */ if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { int width = 14 - strlen (otrust); if (width <= 0) width = 1; tty_fprintf (fp, _("trust: %s"), otrust); tty_fprintf (fp, "%*s", width, ""); } tty_fprintf (fp, _("validity: %s"), trust); tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY && (get_ownertrust (pk) & TRUST_FLAG_DISABLED)) { tty_fprintf (fp, "*** "); tty_fprintf (fp, _("This key has been disabled")); tty_fprintf (fp, "\n"); } } if ((node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) && with_fpr) { print_fingerprint (fp, pk, 2); tty_fprintf (fp, "\n"); } } } show_names (ctrl, fp, keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs); if (do_warn && !nowarn) tty_fprintf (fp, _("Please note that the shown key validity" " is not necessarily correct\n" "unless you restart the program.\n")); xfree (serialno); } /* Display basic key information. This function is suitable to show information on the key without any dependencies on the trustdb or any other internal GnuPG stuff. KEYBLOCK may either be a public or a secret key. This function may be called with KEYBLOCK containing secret keys and thus the printing of "pub" vs. "sec" does only depend on the packet type and not by checking with gpg-agent. */ void show_basic_key_info (KBNODE keyblock) { KBNODE node; int i; char pkstrbuf[PUBKEY_STRING_SIZE]; /* The primary key */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { PKT_public_key *pk = node->pkt->pkt.public_key; /* Note, we use the same format string as in other show functions to make the translation job easier. */ tty_printf ("%s %s/%s ", node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" : node->pkt->pkttype == PKT_PUBLIC_SUBKEY ? "sub" : node->pkt->pkttype == PKT_SECRET_KEY ? "sec" :"ssb", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk)); tty_printf (_("created: %s"), datestr_from_pk (pk)); tty_printf (" "); tty_printf (_("expires: %s"), expirestr_from_pk (pk)); tty_printf ("\n"); print_fingerprint (NULL, pk, 3); tty_printf ("\n"); } } /* The user IDs. */ for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; tty_printf (" "); if (uid->is_revoked) tty_printf ("[%s] ", _("revoked")); else if (uid->is_expired) tty_printf ("[%s] ", _("expired")); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } } } static void show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk = node->pkt->pkt.public_key; tty_printf ("pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk)); } else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_print_utf8_string (uid->name, uid->len); break; } } tty_printf ("\n"); if (pk) print_fingerprint (NULL, pk, 2); if (with_subkeys) { for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("sub %s/%s %s [%s]\n", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); print_fingerprint (NULL, pk, 4); } } } } /* Show a listing of the primary and its subkeys along with their keygrips. */ static void show_key_and_grip (kbnode_t keyblock) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; char *hexgrip; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("%s %s/%s %s [%s]\n", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); if (!hexkeygrip_from_pk (pk, &hexgrip)) { tty_printf (" Keygrip: %s\n", hexgrip); xfree (hexgrip); } } } } /* Show a warning if no uids on the key have the primary uid flag set. */ static void no_primary_warning (KBNODE keyblock) { KBNODE node; int have_primary = 0, uid_count = 0; /* TODO: if we ever start behaving differently with a primary or non-primary attribute ID, we will need to check for attributes here as well. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data == NULL) { uid_count++; if (node->pkt->pkt.user_id->is_primary == 2) { have_primary = 1; break; } } } if (uid_count > 1 && !have_primary) log_info (_ ("WARNING: no user ID has been marked as primary. This command" " may\n cause a different user ID to become" " the assumed primary.\n")); } /* Print a warning if the latest encryption subkey expires soon. This function is called after the expire data of the primary key has been changed. */ static void subkey_expire_warning (kbnode_t keyblock) { u32 curtime = make_timestamp (); kbnode_t node; PKT_public_key *pk; /* u32 mainexpire = 0; */ u32 subexpire = 0; u32 latest_date = 0; for (node = keyblock; node; node = node->next) { /* if (node->pkt->pkttype == PKT_PUBLIC_KEY) */ /* { */ /* pk = node->pkt->pkt.public_key; */ /* mainexpire = pk->expiredate; */ /* } */ if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (!pk->flags.valid) continue; if (pk->flags.revoked) continue; if (pk->timestamp > curtime) continue; /* Ignore future keys. */ if (!(pk->pubkey_usage & PUBKEY_USAGE_ENC)) continue; /* Not an encryption key. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; subexpire = pk->expiredate; } } if (!subexpire) return; /* No valid subkey with an expiration time. */ if (curtime + (10*86400) > subexpire) { log_info (_("WARNING: Your encryption subkey expires soon.\n")); log_info (_("You may want to change its expiration date too.\n")); } } /* * Ask for a new user id, add the self-signature, and update the * keyblock. If UIDSTRING is not NULL the user ID is generated * unattended using that string. UIDSTRING is expected to be utf-8 * encoded and white space trimmed. Returns true if there is a new * user id. */ static int menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock, int photo, const char *photo_name, const char *uidstring) { PKT_user_id *uid; PKT_public_key *pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; KBNODE node; KBNODE pub_where = NULL; gpg_error_t err; if (photo && uidstring) return 0; /* Not allowed. */ for (node = pub_keyblock; node; pub_where = node, node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; } if (!node) /* No subkey. */ pub_where = NULL; log_assert (pk); if (photo) { int hasattrib = 0; for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data != NULL) { hasattrib = 1; break; } /* It is legal but bad for compatibility to add a photo ID to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a photo on a v3 key. Don't bother to ask this if the key already has a photo - any damage has already been done at that point. -dms */ if (pk->version == 3 && !hasattrib) { if (opt.expert) { tty_printf (_("WARNING: This is a PGP2-style key. " "Adding a photo ID may cause some versions\n" " of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_photo.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a photo ID to " "a PGP2-style key.\n")); return 0; } } uid = generate_photo_id (ctrl, pk, photo_name); } else uid = generate_user_id (pub_keyblock, uidstring); if (!uid) { if (uidstring) { write_status_error ("adduid", gpg_error (304)); log_error ("%s", _("Such a user ID already exists on this key!\n")); } return 0; } err = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x13, 0, 0, 0, keygen_add_std_prefs, pk, NULL); if (err) { write_status_error ("keysig", err); log_error ("signing failed: %s\n", gpg_strerror (err)); free_user_id (uid); return 0; } /* Insert/append to public keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode (pkt); if (pub_where) insert_kbnode (pub_where, node, 0); else add_kbnode (pub_keyblock, node); pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; if (pub_where) insert_kbnode (node, new_kbnode (pkt), 0); else add_kbnode (pub_keyblock, new_kbnode (pkt)); return 1; } /* * Remove all selected userids from the keyring */ static void menu_deluid (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { selected = node->flag & NODFLG_SELUID; if (selected) { /* Only cause a trust update if we delete a non-revoked user id */ if (!node->pkt->pkt.user_id->is_revoked) update_trust = 1; delete_kbnode (node); } } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) selected = 0; } commit_kbnode (&pub_keyblock); } static int menu_delsig (KBNODE pub_keyblock) { KBNODE node; PKT_user_id *uid = NULL; int changed = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { uid = (node->flag & NODFLG_SELUID) ? node->pkt->pkt.user_id : NULL; } else if (uid && node->pkt->pkttype == PKT_SIGNATURE) { int okay, valid, selfsig, inv_sig, no_key, other_err; tty_printf ("uid "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); okay = inv_sig = no_key = other_err = 0; if (opt.with_colons) valid = print_and_check_one_sig_colon (pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1); else valid = print_and_check_one_sig (pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1, 0); if (valid) { okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.valid", _("Delete this good signature? (y/N/q)")); /* Only update trust if we delete a good signature. The other two cases do not affect trust. */ if (okay) update_trust = 1; } else if (inv_sig || other_err) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.invalid", _("Delete this invalid signature? (y/N/q)")); else if (no_key) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.unknown", _("Delete this unknown signature? (y/N/q)")); if (okay == -1) break; if (okay && selfsig && !cpr_get_answer_is_yes ("keyedit.delsig.selfsig", _("Really delete this self-signature? (y/N)"))) okay = 0; if (okay) { delete_kbnode (node); changed++; } } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) uid = NULL; } if (changed) { commit_kbnode (&pub_keyblock); tty_printf (ngettext("Deleted %d signature.\n", "Deleted %d signatures.\n", changed), changed); } else tty_printf (_("Nothing deleted.\n")); return changed; } static int menu_clean (KBNODE keyblock, int self_only) { KBNODE uidnode; int modified = 0, select_all = !count_selected_uids (keyblock); for (uidnode = keyblock->next; uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY; uidnode = uidnode->next) { if (uidnode->pkt->pkttype == PKT_USER_ID && (uidnode->flag & NODFLG_SELUID || select_all)) { int uids = 0, sigs = 0; char *user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); clean_one_uid (keyblock, uidnode, opt.verbose, self_only, &uids, &sigs); if (uids) { const char *reason; if (uidnode->pkt->pkt.user_id->is_revoked) reason = _("revoked"); else if (uidnode->pkt->pkt.user_id->is_expired) reason = _("expired"); else reason = _("invalid"); tty_printf (_("User ID \"%s\" compacted: %s\n"), user, reason); modified = 1; } else if (sigs) { tty_printf (ngettext("User ID \"%s\": %d signature removed\n", "User ID \"%s\": %d signatures removed\n", sigs), user, sigs); modified = 1; } else { tty_printf (self_only == 1 ? _("User ID \"%s\": already minimized\n") : _("User ID \"%s\": already clean\n"), user); } xfree (user); } } return modified; } /* * Remove some of the secondary keys */ static void menu_delkey (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { selected = node->flag & NODFLG_SELKEY; if (selected) delete_kbnode (node); } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else selected = 0; } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys are no longer used to certify other keys, so there is no change in trust when revoking/removing them. */ } /* * Ask for a new revoker, create the self-signature and put it into * the keyblock. Returns true if there is a new revoker. */ static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) { PKT_public_key *pk = NULL; PKT_public_key *revoker_pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; struct revocation_key revkey; size_t fprlen; int rc; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = pub_keyblock->pkt->pkt.public_key; if (pk->numrevkeys == 0 && pk->version == 3) { /* It is legal but bad for compatibility to add a revoker to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a revoker on a v3 key. Don't bother to ask this if the key already has a revoker - any damage has already been done at that point. -dms */ if (opt.expert) { tty_printf (_("WARNING: This is a PGP 2.x-style key. " "Adding a designated revoker may cause\n" " some versions of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_revoker.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a designated revoker to " "a PGP 2.x-style key.\n")); return 0; } } for (;;) { char *answer; free_public_key (revoker_pk); revoker_pk = xmalloc_clear (sizeof (*revoker_pk)); tty_printf ("\n"); answer = cpr_get_utf8 ("keyedit.add_revoker", _("Enter the user ID of the designated revoker: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); goto fail; } /* Note that I'm requesting CERT here, which usually implies primary keys only, but some casual testing shows that PGP and GnuPG both can handle a designated revocation from a subkey. */ revoker_pk->req_usage = PUBKEY_USAGE_CERT; rc = get_pubkey_byname (ctrl, NULL, revoker_pk, answer, NULL, NULL, 1, 1); if (rc) { log_error (_("key \"%s\" not found: %s\n"), answer, gpg_strerror (rc)); xfree (answer); continue; } xfree (answer); fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); if (fprlen != 20) { log_error (_("cannot appoint a PGP 2.x style key as a " "designated revoker\n")); continue; } revkey.class = 0x80; if (sensitive) revkey.class |= 0x40; revkey.algid = revoker_pk->pubkey_algo; if (cmp_public_keys (revoker_pk, pk) == 0) { /* This actually causes no harm (after all, a key that designates itself as a revoker is the same as a regular key), but it's easy enough to check. */ log_error (_("you cannot appoint a key as its own " "designated revoker\n")); continue; } keyid_from_pk (pk, NULL); /* Does this revkey already exist? */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i; for (i = 0; i < pk->numrevkeys; i++) { if (memcmp (&pk->revkey[i], &revkey, sizeof (struct revocation_key)) == 0) { char buf[50]; log_error (_("this key has already been designated " "as a revoker\n")); format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf)); write_status_text (STATUS_ALREADY_SIGNED, buf); break; } } if (i < pk->numrevkeys) continue; } print_pubkey_info (NULL, revoker_pk); print_fingerprint (NULL, revoker_pk, 2); tty_printf ("\n"); tty_printf (_("WARNING: appointing a key as a designated revoker " "cannot be undone!\n")); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.add_revoker.okay", _("Are you sure you want to appoint this " "key as a designated revoker? (y/N) "))) continue; free_public_key (revoker_pk); revoker_pk = NULL; break; } rc = make_keysig_packet (&sig, pk, NULL, NULL, pk, 0x1F, 0, 0, 0, keygen_add_revkey, &revkey, NULL); if (rc) { write_status_error ("keysig", rc); log_error ("signing failed: %s\n", gpg_strerror (rc)); goto fail; } /* Insert into public keyblock. */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), PKT_SIGNATURE); return 1; fail: if (sig) free_seckey_enc (sig); free_public_key (revoker_pk); return 0; } static int menu_expire (KBNODE pub_keyblock) { int n1, signumber, rc; u32 expiredate; int mainkey = 0; PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; n1 = count_selected_keys (pub_keyblock); if (n1 > 1) { if (!cpr_get_answer_is_yes ("keyedit.expire_multiple_subkeys.okay", _("Are you sure you want to change the" " expiration time for multiple subkeys? (y/N) "))) return 0; } else if (n1) tty_printf (_("Changing expiration time for a subkey.\n")); else { tty_printf (_("Changing expiration time for the primary key.\n")); mainkey = 1; no_primary_warning (pub_keyblock); } expiredate = ask_expiredate (); /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; signumber = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); main_pk->expiredate = expiredate; } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->flag & NODFLG_SELKEY) { sub_pk = node->pkt->pkt.public_key; sub_pk->expiredate = expiredate; } else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is a self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; signumber++; if ((mainkey && main_pk->version < 4) || (!mainkey && sub_pk->version < 4)) { log_info (_("You can't change the expiration date of a v3 key\n")); return 0; } if (mainkey) rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_expire, main_pk); else rc = update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_expire, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; } } } update_trust = 1; return 1; } /* Change the capability of a selected key. This command should only * be used to rectify badly created keys and as such is not suggested * for general use. */ static int menu_changeusage (kbnode_t keyblock) { int n1, rc; int mainkey = 0; PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; n1 = count_selected_keys (keyblock); if (n1 > 1) { tty_printf (_("You must select exactly one key.\n")); return 0; } else if (n1) tty_printf ("Changing usage of a subkey.\n"); else { tty_printf ("Changing usage of the primary key.\n"); mainkey = 1; } /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->flag & NODFLG_SELKEY) sub_pk = node->pkt->pkt.public_key; else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is the self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; if ((mainkey && main_pk->version < 4) || (!mainkey && sub_pk->version < 4)) { log_info ("You can't change the capabilities of a v3 key\n"); return 0; } if (mainkey) main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0, main_pk->pubkey_usage); else sub_pk->pubkey_usage = ask_key_flags (sub_pk->pubkey_algo, 1, sub_pk->pubkey_usage); if (mainkey) rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_flags, main_pk); else rc = update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_flags, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; break; } } } return 1; } static int menu_backsign (KBNODE pub_keyblock) { int rc, modified = 0; PKT_public_key *main_pk; KBNODE node; u32 timestamp; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); merge_keys_and_selfsig (pub_keyblock); main_pk = pub_keyblock->pkt->pkt.public_key; keyid_from_pk (main_pk, NULL); /* We use the same timestamp for all backsigs so that we don't reveal information about the used machine. */ timestamp = make_timestamp (); for (node = pub_keyblock; node; node = node->next) { PKT_public_key *sub_pk = NULL; KBNODE node2, sig_pk = NULL /*,sig_sk = NULL*/; /* char *passphrase; */ /* Find a signing subkey with no backsig */ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->pkt->pkt.public_key->pubkey_usage & PUBKEY_USAGE_SIG) { if (node->pkt->pkt.public_key->flags.backsig) tty_printf (_ ("signing subkey %s is already cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); else sub_pk = node->pkt->pkt.public_key; } else tty_printf (_("subkey %s does not sign and so does" " not need to be cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); } if (!sub_pk) continue; /* Find the selected selfsig on this subkey */ for (node2 = node->next; node2 && node2->pkt->pkttype == PKT_SIGNATURE; node2 = node2->next) if (node2->pkt->pkt.signature->version >= 4 && node2->pkt->pkt.signature->flags.chosen_selfsig) { sig_pk = node2; break; } if (!sig_pk) continue; /* Find the secret subkey that matches the public subkey */ log_debug ("FIXME: Check whether a secret subkey is available.\n"); /* if (!sub_sk) */ /* { */ /* tty_printf (_("no secret subkey for public subkey %s - ignoring\n"), */ /* keystr_from_pk (sub_pk)); */ /* continue; */ /* } */ /* Now we can get to work. */ rc = make_backsig (sig_pk->pkt->pkt.signature, main_pk, sub_pk, sub_pk, timestamp, NULL); if (!rc) { PKT_signature *newsig; PACKET *newpkt; rc = update_keysig_packet (&newsig, sig_pk->pkt->pkt.signature, main_pk, NULL, sub_pk, main_pk, NULL, NULL); if (!rc) { /* Put the new sig into place on the pubkey */ newpkt = xmalloc_clear (sizeof (*newpkt)); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (sig_pk->pkt); xfree (sig_pk->pkt); sig_pk->pkt = newpkt; modified = 1; } else { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); break; } } else { log_error ("make_backsig failed: %s\n", gpg_strerror (rc)); break; } } return modified; } static int change_primary_uid_cb (PKT_signature * sig, void *opaque) { byte buf[1]; /* first clear all primary uid flags so that we are sure none are * lingering around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID); /* if opaque is set,we want to set the primary id */ if (opaque) { buf[0] = 1; build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, buf, 1); } return 0; } /* * Set the primary uid flag for the selected UID. We will also reset * all other primary uid flags. For this to work with have to update * all the signature timestamps. If we would do this with the current * time, we lose quite a lot of information, so we use a a kludge to * do this: Just increment the timestamp by one second which is * sufficient to updated a signature during import. */ static int menu_set_primary_uid (KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected; int attribute = 0; int modified = 0; if (count_selected_uids (pub_keyblock) != 1) { tty_printf (_("Please select exactly one user ID.\n")); return 0; } main_pk = NULL; uid = NULL; selected = 0; /* Is our selected uid an attribute packet? */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->flag & NODFLG_SELUID) attribute = (node->pkt->pkt.user_id->attrib_data != NULL); for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = node->flag & NODFLG_SELUID; } else if (main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && attribute == (uid->attrib_data != NULL) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced. We can just ignore v3 signatures because they are not able to carry the primary ID flag. We also ignore self-sigs on user IDs that are not of the same type that we are making primary. That is, if we are making a user ID primary, we alter user IDs. If we are making an attribute packet primary, we alter attribute packets. */ /* FIXME: We must make sure that we only have one self-signature per user ID here (not counting revocations) */ PKT_signature *newsig; PACKET *newpkt; const byte *p; int action; /* See whether this signature has the primary UID flag. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (!p) p = parse_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) /* yes */ action = selected ? 0 : -1; else /* no */ action = selected ? 1 : 0; if (action) { int rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, change_primary_uid_cb, action > 0 ? "x" : NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } } return modified; } /* * Set preferences to new values for the selected user IDs */ static int menu_set_preferences (KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; no_primary_warning (pub_keyblock); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user-ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the preferences. */ PKT_signature *newsig; PACKET *newpkt; int rc; rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, keygen_upd_std_prefs, NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } return modified; } static int menu_set_keyserver_url (const char *url, KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer, *uri; no_primary_warning (pub_keyblock); if (url) answer = xstrdup (url); else { answer = cpr_get_utf8 ("keyedit.add_keyserver", _("Enter your preferred keyserver URL: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (ascii_strcasecmp (answer, "none") == 0) uri = NULL; else { struct keyserver_spec *keyserver = NULL; /* Sanity check the format */ keyserver = parse_keyserver_uri (answer, 1); xfree (answer); if (!keyserver) { log_info (_("could not parse keyserver URL\n")); return 0; } uri = xstrdup (keyserver->uri); free_keyserver_spec (keyserver); } select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the subpacket. */ PKT_signature *newsig; PACKET *newpkt; int rc; const byte *p; size_t plen; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &plen); if (p && plen) { tty_printf ("Current preferred keyserver for user" " ID \"%s\": ", user); tty_print_utf8_string (p, plen); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.confirm_keyserver", uri ? _("Are you sure you want to replace it? (y/N) ") : _("Are you sure you want to delete it? (y/N) "))) continue; } else if (uri == NULL) { /* There is no current keyserver URL, so there is no point in trying to un-set it. */ continue; } rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_keyserver_url, uri); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); xfree (uri); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; modified = 1; } xfree (user); } } } xfree (uri); return modified; } static int menu_set_notation (const char *string, KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer; struct notation *notation; no_primary_warning (pub_keyblock); if (string) answer = xstrdup (string); else { answer = cpr_get_utf8 ("keyedit.add_notation", _("Enter the notation: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (!ascii_strcasecmp (answer, "none") || !ascii_strcasecmp (answer, "-")) notation = NULL; /* Delete them all. */ else { notation = string_to_notation (answer, 0); if (!notation) { xfree (answer); return 0; } } xfree (answer); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { PKT_signature *newsig; PACKET *newpkt; int rc, skip = 0, addonly = 1; if (sig->flags.notation) { tty_printf ("Current notations for user ID \"%s\":\n", user); tty_print_notations (-9, sig); } else { tty_printf ("No notations on user ID \"%s\"\n", user); if (notation == NULL) { /* There are no current notations, so there is no point in trying to un-set them. */ continue; } } if (notation) { struct notation *n; int deleting = 0; notation->next = sig_to_notation (sig); for (n = notation->next; n; n = n->next) if (strcmp (n->name, notation->name) == 0) { if (notation->value) { if (strcmp (n->value, notation->value) == 0) { if (notation->flags.ignore) { /* Value match with a delete flag. */ n->flags.ignore = 1; deleting = 1; } else { /* Adding the same notation twice, so don't add it at all. */ skip = 1; tty_printf ("Skipping notation:" " %s=%s\n", notation->name, notation->value); break; } } } else { /* No value, so it means delete. */ n->flags.ignore = 1; deleting = 1; } if (n->flags.ignore) { tty_printf ("Removing notation: %s=%s\n", n->name, n->value); addonly = 0; } } if (!notation->flags.ignore && !skip) tty_printf ("Adding notation: %s=%s\n", notation->name, notation->value); /* We tried to delete, but had no matches. */ if (notation->flags.ignore && !deleting) continue; } else { tty_printf ("Removing all notations\n"); addonly = 0; } if (skip || (!addonly && !cpr_get_answer_is_yes ("keyedit.confirm_notation", _("Proceed? (y/N) ")))) continue; rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_notations, notation); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); free_notation (notation); xfree (user); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt); xfree (node->pkt); node->pkt = newpkt; modified = 1; if (notation) { /* Snip off the notation list from the sig */ free_notation (notation->next); notation->next = NULL; } xfree (user); } } } } free_notation (notation); return modified; } /* * Select one user id or remove all selection if IDX is 0 or select * all if IDX is -1. Returns: True if the selection changed. */ static int menu_select_uid (KBNODE keyblock, int idx) { KBNODE node; int i; if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag |= NODFLG_SELUID; return 1; } else if (idx) /* Toggle. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) if (++i == idx) break; } if (!node) { tty_printf (_("No user ID with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { if (++i == idx) { if ((node->flag & NODFLG_SELUID)) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; } } } } else /* Unselect all */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag &= ~NODFLG_SELUID; } return 1; } /* Search in the keyblock for a uid that matches namehash */ static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash) { byte hash[NAMEHASH_LEN]; KBNODE node; int i; log_assert (strlen (namehash) == NAMEHASH_LEN * 2); for (i = 0; i < NAMEHASH_LEN; i++) hash[i] = hextobyte (&namehash[i * 2]); for (node = keyblock->next; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { namehash_from_uid (node->pkt->pkt.user_id); if (memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN) == 0) { if (node->flag & NODFLG_SELUID) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; break; } } } if (!node) { tty_printf (_("No user ID with hash %s\n"), namehash); return 0; } return 1; } /* * Select secondary keys * Returns: True if the selection changed. */ static int menu_select_key (KBNODE keyblock, int idx, char *p) { KBNODE node; int i, j; int is_hex_digits; is_hex_digits = p && strlen (p) >= 8; if (is_hex_digits) { /* Skip initial spaces. */ while (spacep (p)) p ++; /* If the id starts with 0x accept and ignore it. */ if (p[0] == '0' && p[1] == 'x') p += 2; for (i = 0, j = 0; p[i]; i ++) if (hexdigitp (&p[i])) { p[j] = toupper (p[i]); j ++; } else if (spacep (&p[i])) /* Skip spaces. */ { } else { is_hex_digits = 0; break; } if (is_hex_digits) /* In case we skipped some spaces, add a new NUL terminator. */ { p[j] = 0; /* If we skipped some spaces, make sure that we still have at least 8 characters. */ is_hex_digits = (/* Short keyid. */ strlen (p) == 8 /* Long keyid. */ || strlen (p) == 16 /* Fingerprints are (currently) 32 or 40 characters. */ || strlen (p) >= 32); } } if (is_hex_digits) { int found_one = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { int match = 0; if (strlen (p) == 8 || strlen (p) == 16) { u32 kid[2]; char kid_str[17]; keyid_from_pk (node->pkt->pkt.public_key, kid); format_keyid (kid, strlen (p) == 8 ? KF_SHORT : KF_LONG, kid_str, sizeof (kid_str)); if (strcmp (p, kid_str) == 0) match = 1; } else { char fp[2*MAX_FINGERPRINT_LEN + 1]; hexfingerprint (node->pkt->pkt.public_key, fp, sizeof (fp)); if (strcmp (fp, p) == 0) match = 1; } if (match) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; found_one = 1; } } if (found_one) return 1; tty_printf (_("No subkey with key ID '%s'.\n"), p); return 0; } if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag |= NODFLG_SELKEY; } else if (idx) /* Toggle selection. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) break; } if (!node) { tty_printf (_("No subkey with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; } } } else /* Unselect all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag &= ~NODFLG_SELKEY; } return 1; } static int count_uids_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & flag)) i++; return i; } static int count_keys_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && (node->flag & flag)) i++; return i; } static int count_uids (KBNODE keyblock) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) i++; return i; } /* * Returns true if there is at least one selected user id */ static int count_selected_uids (KBNODE keyblock) { return count_uids_with_flag (keyblock, NODFLG_SELUID); } static int count_selected_keys (KBNODE keyblock) { return count_keys_with_flag (keyblock, NODFLG_SELKEY); } /* Returns how many real (i.e. not attribute) uids are unmarked. */ static int real_uids_left (KBNODE keyblock) { KBNODE node; int real = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODFLG_SELUID) && !node->pkt->pkt.user_id->attrib_data) real++; return real; } /* * Ask whether the signature should be revoked. If the user commits this, * flag bit MARK_A is set on the signature and the user ID. */ static void ask_revoke_sig (KBNODE keyblock, KBNODE node) { int doit = 0; PKT_user_id *uid; PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); if (!unode) { log_error ("Oops: no user ID for signature\n"); return; } uid = unode->pkt->pkt.user_id; if (opt.with_colons) { if (uid->attrib_data) printf ("uat:::::::::%u %lu", uid->numattribs, uid->attrib_len); else { es_printf ("uid:::::::::"); es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); } es_printf ("\n"); print_and_check_one_sig_colon (keyblock, node, NULL, NULL, NULL, NULL, 1); } else { char *p = utf8_to_native (unode->pkt->pkt.user_id->name, unode->pkt->pkt.user_id->len, 0); tty_printf (_("user ID: \"%s\"\n"), p); xfree (p); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), ""); } if (sig->flags.expired) { tty_printf (_("This signature expired on %s.\n"), expirestr_from_sig (sig)); /* Use a different question so we can have different help text */ doit = cpr_get_answer_is_yes ("ask_revoke_sig.expired", _("Are you sure you still want to revoke it? (y/N) ")); } else doit = cpr_get_answer_is_yes ("ask_revoke_sig.one", _("Create a revocation certificate for this signature? (y/N) ")); if (doit) { node->flag |= NODFLG_MARK_A; unode->flag |= NODFLG_MARK_A; } } /* * Display all user ids of the current public key together with signatures * done by one of our keys. Then walk over all this sigs and ask the user * whether he wants to revoke this signature. * Return: True when the keyblock has changed. */ static int menu_revsig (KBNODE keyblock) { PKT_signature *sig; PKT_public_key *primary_pk; KBNODE node; int changed = 0; int rc, any, skip = 1, all = !count_selected_uids (keyblock); struct revocation_reason_info *reason = NULL; log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* First check whether we have any signatures at all. */ any = 0; for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) skip = 0; else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), have_secret_key_with_kid (sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { any = 1; break; } } } if (!any) { tty_printf (_("Not signed by you.\n")); return 0; } /* FIXME: detect duplicates here */ tty_printf (_("You have signed these user IDs on key %s:\n"), keystr_from_pk (keyblock->pkt->pkt.public_key)); for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) { PKT_user_id *uid = node->pkt->pkt.user_id; /* Hmmm: Should we show only UIDs with a signature? */ tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); skip = 0; } else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), have_secret_key_with_kid (sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), sig->flags.revocable ? "" : _(" (non-revocable)")); if (sig->flags.revocable) node->flag |= NODFLG_SELSIG; } else if (sig->sig_class == 0x30) { tty_printf (" "); tty_printf (_("revoked by your key %s on %s\n"), keystr (sig->keyid), datestr_from_sig (sig)); } } } tty_printf ("\n"); /* ask */ for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_SELSIG)) continue; ask_revoke_sig (keyblock, node); } /* present selected */ any = 0; for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_MARK_A)) continue; if (!any) { any = 1; tty_printf (_("You are about to revoke these signatures:\n")); } if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } else if (node->pkt->pkttype == PKT_SIGNATURE) { sig = node->pkt->pkt.signature; tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), "", sig->flags.exportable ? "" : _(" (non-exportable)")); } } if (!any) return 0; /* none selected */ if (!cpr_get_answer_is_yes ("ask_revoke_sig.okay", _("Really create the revocation certificates? (y/N) "))) return 0; /* forget it */ reason = ask_revocation_reason (0, 1, 0); if (!reason) { /* user decided to cancel */ return 0; } /* now we can sign the user ids */ reloop: /* (must use this, because we are modifing the list) */ primary_pk = keyblock->pkt->pkt.public_key; for (node = keyblock; node; node = node->next) { KBNODE unode; PACKET *pkt; struct sign_attrib attrib; PKT_public_key *signerkey; if (!(node->flag & NODFLG_MARK_A) || node->pkt->pkttype != PKT_SIGNATURE) continue; unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); log_assert (unode); /* we already checked this */ memset (&attrib, 0, sizeof attrib); attrib.reason = reason; attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable; node->flag &= ~NODFLG_MARK_A; signerkey = xmalloc_secure_clear (sizeof *signerkey); if (get_seckey (signerkey, node->pkt->pkt.signature->keyid)) { log_info (_("no secret key\n")); free_public_key (signerkey); continue; } rc = make_keysig_packet (&sig, primary_pk, unode->pkt->pkt.user_id, NULL, signerkey, 0x30, 0, 0, 0, sign_mk_attrib, &attrib, NULL); free_public_key (signerkey); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ update_trust = 1; /* Are we revoking our own uid? */ if (primary_pk->keyid[0] == sig->keyid[0] && primary_pk->keyid[1] == sig->keyid[1]) unode->pkt->pkt.user_id->is_revoked = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (unode, new_kbnode (pkt), 0); goto reloop; } release_revocation_reason_info (reason); return changed; } /* return 0 if revocation of NODE (which must be a User ID) was successful, non-zero if there was an error. *modified will be set to 1 if a change was made. */ static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; gpg_error_t rc; if (node->pkt->pkttype != PKT_USER_ID) { rc = gpg_error (GPG_ERR_NO_USER_ID); write_status_error ("keysig", rc); log_error (_("tried to revoke a non-user ID: %s\n"), gpg_strerror (rc)); return 1; } else { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->is_revoked) { char *user = utf8_to_native (uid->name, uid->len, 0); log_info (_("user ID \"%s\" is already revoked\n"), user); xfree (user); } else { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; u32 timestamp = make_timestamp (); if (uid->created >= timestamp) { /* Okay, this is a problem. The user ID selfsig was created in the future, so we need to warn the user and set our revocation timestamp one second after that so everything comes out clean. */ log_info (_("WARNING: a user ID signature is dated %d" " seconds in the future\n"), uid->created - timestamp); timestamp = uid->created + 1; } memset (&attrib, 0, sizeof attrib); /* should not need to cast away const here; but revocation_reason_build_cb needs to take a non-const void* in order to meet the function signtuare for the mksubpkt argument to make_keysig_packet */ attrib.reason = (struct revocation_reason_info *)reason; rc = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x30, 0, timestamp, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); return 1; } else { pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); #ifndef NO_TRUST_MODELS /* If the trustdb has an entry for this key+uid then the trustdb needs an update. */ if (!update_trust - && ((get_validity (ctrl, pk, uid, NULL, 0) & TRUST_MASK) + && ((get_validity (ctrl, keyblock, pk, uid, NULL, 0) + & TRUST_MASK) >= TRUST_UNDEFINED)) update_trust = 1; #endif /*!NO_TRUST_MODELS*/ node->pkt->pkt.user_id->is_revoked = 1; if (modified) *modified = 1; } } return 0; } } /* Revoke a user ID (i.e. revoke a user ID selfsig). Return true if keyblock changed. */ static int menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; /* Note that this is correct as per the RFCs, but nevertheless somewhat meaningless in the real world. 1991 did define the 0x30 sig class, but PGP 2.x did not actually implement it, so it would probably be safe to use v4 revocations everywhere. -ds */ for (node = pub_keyblock; node; node = node->next) if (pk->version > 3 || (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->selfsigversion > 3)) { if ((reason = ask_revocation_reason (0, 1, 4))) break; else goto leave; } reloop: /* (better this way because we are modifying the keyring) */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID)) { int modified = 0; rc = core_revuid (ctrl, pub_keyblock, node, reason, &modified); if (rc) goto leave; if (modified) { node->flag &= ~NODFLG_SELUID; changed = 1; goto reloop; } } if (changed) commit_kbnode (&pub_keyblock); leave: release_revocation_reason_info (reason); return changed; } /* * Revoke the whole key. */ static int menu_revkey (KBNODE pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; int rc, changed = 0; struct revocation_reason_info *reason; PACKET *pkt; PKT_signature *sig; if (pk->flags.revoked) { tty_printf (_("Key %s is already revoked.\n"), keystr_from_pk (pk)); return 0; } reason = ask_revocation_reason (1, 0, 0); /* user decided to cancel */ if (!reason) return 0; rc = make_keysig_packet (&sig, pk, NULL, NULL, pk, 0x20, 0, 0, 0, revocation_reason_build_cb, reason, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto scram; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), 0); commit_kbnode (&pub_keyblock); update_trust = 1; scram: release_revocation_reason_info (reason); return changed; } static int menu_revsubkey (KBNODE pub_keyblock) { PKT_public_key *mainpk; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; reason = ask_revocation_reason (1, 0, 0); if (!reason) return 0; /* User decided to cancel. */ reloop: /* (better this way because we are modifing the keyring) */ mainpk = pub_keyblock->pkt->pkt.public_key; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (node->flag & NODFLG_SELKEY)) { PACKET *pkt; PKT_signature *sig; PKT_public_key *subpk = node->pkt->pkt.public_key; struct sign_attrib attrib; if (subpk->flags.revoked) { tty_printf (_("Subkey %s is already revoked.\n"), keystr_from_pk (subpk)); continue; } memset (&attrib, 0, sizeof attrib); attrib.reason = reason; node->flag &= ~NODFLG_SELKEY; rc = make_keysig_packet (&sig, mainpk, NULL, subpk, mainpk, 0x28, 0, 0, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); goto reloop; } } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys no longer are used to certify other keys, so there is no change in trust when revoking/removing them */ release_revocation_reason_info (reason); return changed; } /* Note that update_ownertrust is going to mark the trustdb dirty when enabling or disabling a key. This is arguably sub-optimal as disabled keys are still counted in the web of trust, but perhaps not worth adding extra complexity to change. -ds */ #ifndef NO_TRUST_MODELS static int enable_disable_key (KBNODE keyblock, int disable) { PKT_public_key *pk = find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt.public_key; unsigned int trust, newtrust; trust = newtrust = get_ownertrust (pk); newtrust &= ~TRUST_FLAG_DISABLED; if (disable) newtrust |= TRUST_FLAG_DISABLED; if (trust == newtrust) return 0; /* already in that state */ update_ownertrust (pk, newtrust); return 0; } #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock) { KBNODE node; int select_all = !count_selected_uids (keyblock); int count = 0; PKT_public_key *pk = NULL; /* Look for the public key first. We have to be really, really, explicit as to which photo this is, and what key it is a UID on since people may want to sign it. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; count++; if ((select_all || (node->flag & NODFLG_SELUID)) && uid->attribs != NULL) { int i; for (i = 0; i < uid->numattribs; i++) { byte type; u32 size; if (uid->attribs[i].type == ATTRIB_IMAGE && parse_image_header (&uid->attribs[i], &type, &size)) { tty_printf (_("Displaying %s photo ID of size %ld for " "key %s (uid %d)\n"), image_type_to_string (type, 1), (ulong) size, keystr_from_pk (pk), count); show_photos (ctrl, &uid->attribs[i], 1, pk, uid); } } } } } } diff --git a/g10/keylist.c b/g10/keylist.c index 0523be090..a5fdc06a8 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,1922 +1,1922 @@ /* keylist.c - Print information about OpenPGP keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2008, 2010, 2012 Free Software Foundation, Inc. * Copyright (C) 2013, 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM # include /* for setmode() */ #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "keydb.h" #include "photoid.h" #include "util.h" #include "ttyio.h" #include "trustdb.h" #include "main.h" #include "i18n.h" #include "status.h" #include "call-agent.h" #include "mbox-util.h" #include "zb32.h" #include "tofu.h" static void list_all (ctrl_t, int, int); static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret); static void locate_one (ctrl_t ctrl, strlist_t names); static void print_card_serialno (const char *serialno); struct keylist_context { int check_sigs; /* If set signatures shall be verified. */ int good_sigs; /* Counter used if CHECK_SIGS is set. */ int inv_sigs; /* Counter used if CHECK_SIGS is set. */ int no_key; /* Counter used if CHECK_SIGS is set. */ int oth_err; /* Counter used if CHECK_SIGS is set. */ int no_validity; /* Do not show validity. */ }; static void list_keyblock (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx); /* The stream used to write attribute packets to. */ static estream_t attrib_fp; /* Release resources from a keylist context. */ static void keylist_context_release (struct keylist_context *listctx) { (void)listctx; /* Nothing to release. */ } /* List the keys. If list is NULL, all available keys are listed. With LOCATE_MODE set the locate algorithm is used to find a key. */ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode) { #ifndef NO_TRUST_MODELS if (opt.with_colons) { byte trust_model, marginals, completes, cert_depth, min_cert_level; ulong created, nextcheck; read_trust_options (&trust_model, &created, &nextcheck, &marginals, &completes, &cert_depth, &min_cert_level); es_fprintf (es_stdout, "tru:"); if (nextcheck && nextcheck <= make_timestamp ()) es_fprintf (es_stdout, "o"); if (trust_model != opt.trust_model) es_fprintf (es_stdout, "t"); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { if (marginals != opt.marginals_needed) es_fprintf (es_stdout, "m"); if (completes != opt.completes_needed) es_fprintf (es_stdout, "c"); if (cert_depth != opt.max_cert_depth) es_fprintf (es_stdout, "d"); if (min_cert_level != opt.min_cert_level) es_fprintf (es_stdout, "l"); } es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck); /* Only show marginals, completes, and cert_depth in the classic or PGP trust models since they are not meaningful otherwise. */ if (trust_model == TM_PGP || trust_model == TM_CLASSIC) es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth); es_fprintf (es_stdout, "\n"); } #endif /*!NO_TRUST_MODELS*/ /* We need to do the stale check right here because it might need to update the keyring while we already have the keyring open. This is very bad for W32 because of a sharing violation. For real OSes it might lead to false results if we are later listing a keyring which is associated with the inode of a deleted file. */ check_trustdb_stale (ctrl); #ifdef USE_TOFU tofu_begin_batch_update (ctrl); #endif if (locate_mode) locate_one (ctrl, list); else if (!list) list_all (ctrl, 0, opt.with_secret); else list_one (ctrl, list, 0, opt.with_secret); #ifdef USE_TOFU tofu_end_batch_update (ctrl); #endif } void secret_key_list (ctrl_t ctrl, strlist_t list) { (void)ctrl; check_trustdb_stale (ctrl); if (!list) list_all (ctrl, 1, 0); else /* List by user id */ list_one (ctrl, list, 1, 0); } char * format_seckey_info (PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; char *info; keyid_from_pk (pk, keyid); p = get_user_id_native (keyid); info = xtryasprintf ("sec %s/%s %s %s", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); return info; } void print_seckey_info (PKT_public_key *pk) { char *p = format_seckey_info (pk); tty_printf ("\n%s\n", p); xfree (p); } /* Print information about the public key. With FP passed as NULL, the tty output interface is used, otherwise output is directted to the given stream. */ void print_pubkey_info (estream_t fp, PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; keyid_from_pk (pk, keyid); /* If the pk was chosen by a particular user ID, that is the one to print. */ if (pk->user_id) p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); else p = get_user_id_native (keyid); if (fp) tty_printf ("\n"); tty_fprintf (fp, "%s %s/%s %s %s\n", pk->flags.primary? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); } /* Print basic information of a secret key including the card serial number information. */ #ifdef ENABLE_CARD_SUPPORT void print_card_key_info (estream_t fp, kbnode_t keyblock) { kbnode_t node; char *hexgrip; char *serialno; int s2k_char; char pkstrbuf[PUBKEY_STRING_SIZE]; int indent; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { int rc; PKT_public_key *pk = node->pkt->pkt.public_key; serialno = NULL; rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) { log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); s2k_char = '?'; } else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) s2k_char = serialno? '>':' '; else s2k_char = '#'; /* Key not found. */ tty_fprintf (fp, "%s%c %s/%s %n", node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb", s2k_char, pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), &indent); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); if (serialno) { tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s", serialno); } tty_fprintf (fp, "\n"); xfree (hexgrip); xfree (serialno); } } } #endif /*ENABLE_CARD_SUPPORT*/ /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { char status[40]; /* Don't print these. */ if (len > 256) return; snprintf (status, sizeof status, "%d %u %u ", type, flags, (unsigned int) len); write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0); } /* Print a policy URL. Allowed values for MODE are: * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_policy_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit))) { if (mode != 2) { int i; const char *str; for (i = 0; i < indent; i++) es_putc (' ', fp); if (crit) str = _("Critical signature policy: "); else str = _("Signature policy: "); if (mode) log_info ("%s", str); else es_fprintf (fp, "%s", str); print_utf8_buffer (fp, p, len); es_fprintf (fp, "\n"); } if (mode) write_status_buffer (STATUS_POLICY_URL, p, len, 0); } } /* mode=0 for stdout. mode=1 for log_info + status messages mode=2 for status messages only */ /* TODO: use this */ void show_keyserver_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq, &crit))) { if (mode != 2) { int i; const char *str; for (i = 0; i < indent; i++) es_putc (' ', es_stdout); if (crit) str = _("Critical preferred keyserver: "); else str = _("Preferred keyserver: "); if (mode) log_info ("%s", str); else es_fprintf (es_stdout, "%s", str); print_utf8_buffer (fp, p, len); es_fprintf (fp, "\n"); } if (mode) status_one_subpacket (SIGSUBPKT_PREF_KS, len, (crit ? 0x02 : 0) | 0x01, p); } } /* mode=0 for stdout. mode=1 for log_info + status messages mode=2 for status messages only Defined bits in WHICH: 1 == standard notations 2 == user notations */ void show_notation (PKT_signature * sig, int indent, int mode, int which) { estream_t fp = mode ? log_get_stream () : es_stdout; notation_t nd, notations; if (which == 0) which = 3; notations = sig_to_notation (sig); /* There may be multiple notations in the same sig. */ for (nd = notations; nd; nd = nd->next) { if (mode != 2) { int has_at = !!strchr (nd->name, '@'); if ((which & 1 && !has_at) || (which & 2 && has_at)) { int i; const char *str; for (i = 0; i < indent; i++) es_putc (' ', es_stdout); if (nd->flags.critical) str = _("Critical signature notation: "); else str = _("Signature notation: "); if (mode) log_info ("%s", str); else es_fprintf (es_stdout, "%s", str); /* This is all UTF8 */ print_utf8_buffer (fp, nd->name, strlen (nd->name)); es_fprintf (fp, "="); print_utf8_buffer (fp, nd->value, strlen (nd->value)); /* (We need to use log_printf so that the next call to a log function does not insert an extra LF.) */ if (mode) log_printf ("\n"); else es_putc ('\n', fp); } } if (mode) { write_status_buffer (STATUS_NOTATION_NAME, nd->name, strlen (nd->name), 0); if (nd->flags.critical || nd->flags.human) write_status_text (STATUS_NOTATION_FLAGS, nd->flags.critical && nd->flags.human? "1 1" : nd->flags.critical? "1 0" : "0 1"); write_status_buffer (STATUS_NOTATION_DATA, nd->value, strlen (nd->value), 50); } } free_notation (notations); } static void print_signature_stats (struct keylist_context *s) { if (!s->check_sigs) return; /* Signature checking was not requested. */ if (s->good_sigs) log_info (ngettext("%d good signature\n", "%d good signatures\n", s->good_sigs), s->good_sigs); if (s->inv_sigs) log_info (ngettext("%d bad signature\n", "%d bad signatures\n", s->inv_sigs), s->inv_sigs); if (s->no_key) log_info (ngettext("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", s->no_key), s->no_key); if (s->oth_err) log_info (ngettext("%d signature not checked due to an error\n", "%d signatures not checked due to errors\n", s->oth_err), s->oth_err); } /* List all keys. If SECRET is true only secret keys are listed. If MARK_SECRET is true secret keys are indicated in a public key listing. */ static void list_all (ctrl_t ctrl, int secret, int mark_secret) { KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc = 0; int any_secret; const char *lastresname, *resname; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; hd = keydb_new (); if (!hd) rc = gpg_error_from_syserror (); else rc = keydb_search_first (hd); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); goto leave; } lastresname = NULL; do { rc = keydb_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if (secret || mark_secret) any_secret = !agent_probe_any_secret_key (NULL, keyblock); else any_secret = 0; if (secret && !any_secret) ; /* Secret key listing requested but this isn't one. */ else { if (!opt.with_colons) { resname = keydb_get_resource_name (hd); if (lastresname != resname) { int i; es_fprintf (es_stdout, "%s\n", resname); for (i = strlen (resname); i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); lastresname = resname; } } merge_keys_and_selfsig (keyblock); list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); if (keydb_get_skipped_counter (hd)) log_info (ngettext("Warning: %lu key skipped due to its large size\n", "Warning: %lu keys skipped due to their large sizes\n", keydb_get_skipped_counter (hd)), keydb_get_skipped_counter (hd)); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); leave: keylist_context_release (&listctx); release_kbnode (keyblock); keydb_release (hd); } static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) { int rc = 0; KBNODE keyblock = NULL; GETKEY_CTX ctx; const char *resname; const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) listctx.check_sigs = 1; /* fixme: using the bynames function has the disadvantage that we * don't know wether one of the names given was not found. OTOH, * this function has the advantage to list the names in the * sequence as defined by the keyDB and does not duplicate * outputs. A solution could be do test whether all given have * been listed (this needs a way to use the keyDB search * functions) or to have the search function return indicators for * found names. Yet another way is to use the keydb search * facilities directly. */ rc = getkey_bynames (&ctx, NULL, names, secret, &keyblock); if (rc) { log_error ("error reading key: %s\n", gpg_strerror (rc)); getkey_end (ctx); return; } do { if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons) { resname = keydb_get_resource_name (get_ctx_handle (ctx)); es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname); for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } list_keyblock (ctrl, keyblock, secret, mark_secret, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (!getkey_next (ctx, NULL, &keyblock)); getkey_end (ctx); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void locate_one (ctrl_t ctrl, strlist_t names) { int rc = 0; strlist_t sl; GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; for (sl = names; sl; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY) log_error ("error reading key: %s\n", gpg_strerror (rc)); else if (opt.verbose) log_info (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (rc)); } else { do { list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (ctx && !getkey_next (ctx, NULL, &keyblock)); getkey_end (ctx); ctx = NULL; } } if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void print_key_data (PKT_public_key * pk) { int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0; int i; for (i = 0; i < n; i++) { es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i])); mpi_print (es_stdout, pk->pkey[i], 1); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } static void print_capabilities (PKT_public_key *pk, KBNODE keyblock) { unsigned int use = pk->pubkey_usage; int c_printed = 0; if (use & PUBKEY_USAGE_ENC) es_putc ('e', es_stdout); if (use & PUBKEY_USAGE_SIG) { es_putc ('s', es_stdout); if (pk->flags.primary) { es_putc ('c', es_stdout); /* The PUBKEY_USAGE_CERT flag was introduced later and we used to always print 'c' for a primary key. To avoid any regression here we better track whether we printed 'c' already. */ c_printed = 1; } } if ((use & PUBKEY_USAGE_CERT) && !c_printed) es_putc ('c', es_stdout); if ((use & PUBKEY_USAGE_AUTH)) es_putc ('a', es_stdout); if ((use & PUBKEY_USAGE_UNKNOWN)) es_putc ('?', es_stdout); if (keyblock) { /* Figure out the usable capabilities. */ KBNODE k; int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = k->pkt->pkt.public_key; if (pk->flags.primary) disabled = pk_is_disabled (pk); if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired) { if (pk->pubkey_usage & PUBKEY_USAGE_ENC) enc = 1; if (pk->pubkey_usage & PUBKEY_USAGE_SIG) { sign = 1; if (pk->flags.primary) cert = 1; } if (pk->pubkey_usage & PUBKEY_USAGE_CERT) cert = 1; if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) auth = 1; } } } if (enc) es_putc ('E', es_stdout); if (sign) es_putc ('S', es_stdout); if (cert) es_putc ('C', es_stdout); if (auth) es_putc ('A', es_stdout); if (disabled) es_putc ('D', es_stdout); } es_putc (':', es_stdout); } /* FLAGS: 0x01 hashed 0x02 critical */ static void print_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { size_t i; es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len); for (i = 0; i < len; i++) { /* printable ascii other than : and % */ if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%') es_fprintf (es_stdout, "%c", buf[i]); else es_fprintf (es_stdout, "%%%02X", buf[i]); } es_fprintf (es_stdout, "\n"); } void print_subpackets_colon (PKT_signature * sig) { byte *i; log_assert (opt.show_subpackets); for (i = opt.show_subpackets; *i; i++) { const byte *p; size_t len; int seq, crit; seq = 0; while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p); seq = 0; while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p); } } void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) { int i; if (!attrib_fp) return; for (i = 0; i < uid->numattribs; i++) { if (is_status_enabled ()) { byte array[MAX_FINGERPRINT_LEN], *p; char buf[(MAX_FINGERPRINT_LEN * 2) + 90]; size_t j, n; if (!pk) BUG (); fingerprint_from_pk (pk, array, &n); p = array; for (j = 0; j < n; j++, p++) sprintf (buf + 2 * j, "%02X", *p); sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u", (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1, uid->numattribs, (ulong) uid->created, (ulong) uid->expiredate, ((uid->is_primary ? 0x01 : 0) | (uid-> is_revoked ? 0x02 : 0) | (uid->is_expired ? 0x04 : 0))); write_status_text (STATUS_ATTRIBUTE, buf); } es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp); es_fflush (attrib_fp); } } static void list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, struct keylist_context *listctx) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; int skip_sigs = 0; char *hexgrip = NULL; char *serialno = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { /* Encode some info about the secret key in SECRET. */ if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } if (!listctx->no_validity) check_trustdb_stale (ctrl); /* Print the "pub" line and in KF_NONE mode the fingerprint. */ print_key_line (es_stdout, pk, secret); if (fpr) print_fingerprint (NULL, pk, 0); if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (serialno) print_card_serialno (serialno); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int indent; int kl = opt.keyid_format == KF_NONE? 10 : keystrlen (); if ((uid->is_expired || uid->is_revoked) && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS)) { skip_sigs = 1; continue; } else skip_sigs = 0; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if ((uid->is_revoked || uid->is_expired) || ((opt.list_options & LIST_SHOW_UID_VALIDITY) && !listctx->no_validity)) { const char *validity; validity = uid_trust_string_fixed (ctrl, pk, uid); indent = ((kl + (opt.legacy_list_mode? 9:11)) - atoi (uid_trust_string_fixed (ctrl, NULL, NULL))); if (indent < 0 || indent > 40) indent = 0; es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity); } else { indent = kl + (opt.legacy_list_mode? 10:12); es_fprintf (es_stdout, "uid%*s", indent, ""); } print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); if (opt.with_wkd_hash) { char *mbox, *hash, *p; char hashbuf[32]; mbox = mailbox_from_userid (uid->name); if (mbox && (p = strchr (mbox, '@'))) { *p++ = 0; gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); hash = zb32_encode (hashbuf, 8*20); if (hash) { es_fprintf (es_stdout, " %*s%s@%s\n", indent, "", hash, p); xfree (hash); } } xfree (mbox); } if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL) show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk2 = node->pkt->pkt.public_key; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { skip_sigs = 1; continue; } else skip_sigs = 0; xfree (serialno); serialno = NULL; xfree (hexgrip); hexgrip = NULL; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk2, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = '2'; /* Key not found. */ } /* Print the "sub" line. */ print_key_line (es_stdout, pk2, secret); if (fpr > 1 || opt.with_subkey_fingerprint) { print_fingerprint (NULL, pk2, 0); if (serialno) print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; if (listctx->check_sigs) { rc = check_key_signature (keyblock, node, NULL); switch (gpg_err_code (rc)) { case 0: listctx->good_sigs++; sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: listctx->inv_sigs++; sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: listctx->no_key++; continue; default: listctx->oth_err++; sigrc = '%'; break; } /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keyedit.c:print_and_check_one_sig */ } else { rc = 0; sigrc = ' '; } if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig " "[unexpected signature class 0x%02x]\n", sig->sig_class); continue; } es_fputs (sigstr, es_stdout); es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if (opt.list_options & LIST_SHOW_SIG_EXPIRE) es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); es_fprintf (es_stdout, " "); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (!opt.fast_list_mode) { size_t n; char *p = get_user_id (sig->keyid, &n); print_utf8_buffer (es_stdout, p, n); xfree (p); } es_putc ('\n', es_stdout); if (sig->flags.policy_url && (opt.list_options & LIST_SHOW_POLICY_URLS)) show_policy_url (sig, 3, 0); if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) show_notation (sig, 3, 0, ((opt. list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt. list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); /* fixme: check or list other sigs here */ } } es_putc ('\n', es_stdout); xfree (serialno); xfree (hexgrip); } void print_revokers (estream_t fp, PKT_public_key * pk) { /* print the revoker record */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i, j; for (i = 0; i < pk->numrevkeys; i++) { byte *p; es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid); p = pk->revkey[i].fpr; for (j = 0; j < 20; j++, p++) es_fprintf (fp, "%02X", *p); es_fprintf (fp, ":%02x%s:\n", pk->revkey[i].class, (pk->revkey[i].class & 0x40) ? "s" : ""); } } } /* List a key in colon mode. If SECRET is true this is a secret key record (i.e. requested via --list-secret-key). If HAS_SECRET a secret key is available even if SECRET is not set. */ static void list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; u32 keyid[2]; int trustletter = 0; int trustletter_print; int ownertrust_print; int ulti_hack = 0; int i; char *hexgrip_buffer = NULL; const char *hexgrip = NULL; char *serialno = NULL; int stubkey; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); /* In the error case we print an empty string so that we have a * "grp" record for each and subkey - even if it is empty. This * may help to prevent sync problems. */ hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret || has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk, keyid); if (!pk->flags.valid) trustletter_print = 'i'; else if (pk->flags.revoked) trustletter_print = 'r'; else if (pk->has_expired) trustletter_print = 'e'; else if (opt.fast_list_mode || opt.no_expensive_trust_checks) trustletter_print = 0; else { - trustletter = get_validity_info (ctrl, pk, NULL); + trustletter = get_validity_info (ctrl, keyblock, pk, NULL); if (trustletter == 'u') ulti_hack = 1; trustletter_print = trustletter; } if (!opt.fast_list_mode && !opt.no_expensive_trust_checks) ownertrust_print = get_ownertrust_info (pk); else ownertrust_print = 0; es_fputs (secret? "sec:":"pub:", es_stdout); if (trustletter_print) es_putc (trustletter_print, es_stdout); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::", nbits_from_pk (pk), pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], colon_datestr_from_pk (pk), colon_strtime (pk->expiredate)); if (ownertrust_print) es_putc (ownertrust_print, es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); print_capabilities (pk, keyblock); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); if (!name) name = curve; es_fputs (name, es_stdout); xfree (curve); } es_putc (':', es_stdout); /* End of field 17. */ es_putc (':', es_stdout); /* End of field 18. */ es_putc ('\n', es_stdout); print_revokers (es_stdout, pk); print_fingerprint (NULL, pk, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int uid_validity; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if (uid->is_revoked) uid_validity = 'r'; else if (uid->is_expired) uid_validity = 'e'; else if (opt.no_expensive_trust_checks) uid_validity = 0; else if (ulti_hack) uid_validity = 'u'; else - uid_validity = get_validity_info (ctrl, pk, uid); + uid_validity = get_validity_info (ctrl, keyblock, pk, uid); es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout); if (uid_validity) es_putc (uid_validity, es_stdout); es_fputs ("::::", es_stdout); es_fprintf (es_stdout, "%s:", colon_strtime (uid->created)); es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate)); namehash_from_uid (uid); for (i = 0; i < 20; i++) es_fprintf (es_stdout, "%02X", uid->namehash[i]); es_fprintf (es_stdout, "::"); if (uid->attrib_data) es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); es_putc (':', es_stdout); es_putc ('\n', es_stdout); #ifdef USE_TOFU if (!uid->attrib_data && opt.with_tofu_info && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)) { /* Print a "tfs" record. */ tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name); } #endif /*USE_TOFU*/ } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 keyid2[2]; PKT_public_key *pk2; int need_hexgrip = !!hexgrip; pk2 = node->pkt->pkt.public_key; xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL; xfree (serialno); serialno = NULL; if (need_hexgrip || secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk2, keyid2); es_fputs (secret? "ssb:":"sub:", es_stdout); if (!pk2->flags.valid) es_putc ('i', es_stdout); else if (pk2->flags.revoked) es_putc ('r', es_stdout); else if (pk2->has_expired) es_putc ('e', es_stdout); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) ; else { /* TRUSTLETTER should always be defined here. */ if (trustletter) es_fprintf (es_stdout, "%c", trustletter); } es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::", nbits_from_pk (pk2), pk2->pubkey_algo, (ulong) keyid2[0], (ulong) keyid2[1], colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate) /* fixme: add LID and ownertrust here */ ); print_capabilities (pk2, NULL); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA || pk2->pubkey_algo == PUBKEY_ALGO_ECDH) { char *curve = openpgp_oid_to_str (pk2->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); if (!name) name = curve; es_fputs (name, es_stdout); xfree (curve); } es_putc (':', es_stdout); /* End of field 17. */ es_putc ('\n', es_stdout); print_fingerprint (NULL, pk2, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc, fprokay = 0; char *sigstr; size_t fplen; byte fparray[MAX_FINGERPRINT_LEN]; char *siguid; size_t siguidlen; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig::::::::::%02x%c:\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); continue; } if (opt.check_sigs) { PKT_public_key *signer_pk = NULL; fflush (stdout); if (opt.no_sig_cache) signer_pk = xmalloc_clear (sizeof (PKT_public_key)); rc = check_key_signature2 (keyblock, node, NULL, signer_pk, NULL, NULL, NULL); switch (gpg_err_code (rc)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if (opt.no_sig_cache) { if (!rc) { fingerprint_from_pk (signer_pk, fparray, &fplen); fprokay = 1; } free_public_key (signer_pk); } } else { rc = 0; sigrc = ' '; } if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) siguid = get_user_id (sig->keyid, &siguidlen); else { siguid = NULL; siguidlen = 0; } es_fputs (sigstr, es_stdout); es_putc (':', es_stdout); if (sigrc != ' ') es_putc (sigrc, es_stdout); es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (sig->trust_depth || sig->trust_value) es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value); es_fprintf (es_stdout, ":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_fprintf (es_stdout, ":"); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (opt.no_sig_cache && opt.check_sigs && fprokay) { for (i = 0; i < fplen; i++) es_fprintf (es_stdout, "%02X", fparray[i]); } es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ xfree (siguid); } } xfree (hexgrip_buffer); xfree (serialno); } /* * Reorder the keyblock so that the primary user ID (and not attribute * packet) comes first. Fixme: Replace this by a generic sort * function. */ static void do_reorder_keyblock (KBNODE keyblock, int attr) { KBNODE primary = NULL, primary0 = NULL, primary2 = NULL; KBNODE last, node; for (node = keyblock; node; primary0 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && ((attr && node->pkt->pkt.user_id->attrib_data) || (!attr && !node->pkt->pkt.user_id->attrib_data)) && node->pkt->pkt.user_id->is_primary) { primary = primary2 = node; for (node = node->next; node; primary2 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { break; } } break; } } if (!primary) return; /* No primary key flag found (should not happen). */ for (last = NULL, node = keyblock; node; last = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; } log_assert (node); log_assert (last); /* The user ID is never the first packet. */ log_assert (primary0); /* Ditto (this is the node before primary). */ if (node == primary) return; /* Already the first one. */ last->next = primary; primary0->next = primary2->next; primary2->next = node; } void reorder_keyblock (KBNODE keyblock) { do_reorder_keyblock (keyblock, 1); do_reorder_keyblock (keyblock, 0); } static void list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { reorder_keyblock (keyblock); if (opt.with_colons) list_keyblock_colon (ctrl, keyblock, secret, has_secret); else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); if (secret) es_fflush (es_stdout); } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); } /* Print an hex digit in ICAO spelling. */ static void print_icao_hexdigit (estream_t fp, int c) { static const char *list[16] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot" }; tty_fprintf (fp, "%s", list[c&15]); } /* * Function to print the finperprint. * mode 0: as used in key listings, opt.with_colons is honored * 1: print using log_info () * 2: direct use of tty * 3: direct use of tty but only primary key. * 4: direct use of tty but only subkey. * 10: Same as 0 but with_colons etc is ignored. * 20: Same as 0 but using a compact format. * * Modes 1 and 2 will try and print both subkey and primary key * fingerprints. A MODE with bit 7 set is used internally. If * OVERRIDE_FP is not NULL that stream will be used in 0 instead * of es_stdout or instead of the TTY in modes 2 and 3. */ void print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode) { char hexfpr[2*MAX_FINGERPRINT_LEN+1]; char *p; size_t i; estream_t fp; const char *text; int primary = 0; int with_colons = opt.with_colons; int with_icao = opt.with_icao_spelling; int compact = 0; if (mode == 10) { mode = 0; with_colons = 0; with_icao = 0; } else if (mode == 20) { mode = 0; with_colons = 0; compact = 1; } if (!opt.fingerprint && !opt.with_fingerprint && opt.with_subkey_fingerprint) compact = 1; if (pk->main_keyid[0] == pk->keyid[0] && pk->main_keyid[1] == pk->keyid[1]) primary = 1; /* Just to be safe */ if ((mode & 0x80) && !primary) { log_error ("primary key is not really primary!\n"); return; } mode &= ~0x80; if (!primary && (mode == 1 || mode == 2)) { PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk)); get_pubkey (primary_pk, pk->main_keyid); print_fingerprint (override_fp, primary_pk, (mode | 0x80)); free_public_key (primary_pk); } if (mode == 1) { fp = log_get_stream (); if (primary) text = _("Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 2) { fp = override_fp; /* Use tty or given stream. */ if (primary) /* TRANSLATORS: this should fit into 24 bytes so that the * fingerprint data is properly aligned with the user ID */ text = _(" Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 3) { fp = override_fp; /* Use tty or given stream. */ text = _(" Key fingerprint ="); } else if (mode == 4) { fp = override_fp; /* Use tty or given stream. */ text = _(" Subkey fingerprint:"); } else { fp = override_fp? override_fp : es_stdout; if (opt.keyid_format == KF_NONE) { text = " "; /* To indent ICAO spelling. */ compact = 1; } else text = _(" Key fingerprint ="); } hexfingerprint (pk, hexfpr, sizeof hexfpr); if (with_colons && !mode) { es_fprintf (fp, "fpr:::::::::%s:", hexfpr); } else if (compact && !opt.fingerprint && !opt.with_fingerprint) { tty_fprintf (fp, "%*s%s", 6, "", hexfpr); } else { char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1]; format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr); if (compact) tty_fprintf (fp, "%*s%s", 6, "", fmtfpr); else tty_fprintf (fp, "%s %s", text, fmtfpr); } tty_fprintf (fp, "\n"); if (!with_colons && with_icao) { ; tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, ""); for (i = 0, p = hexfpr; *p; i++, p++) { if (!i) ; else if (!(i%8)) tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, ""); else if (!(i%4)) tty_fprintf (fp, " "); else tty_fprintf (fp, " "); print_icao_hexdigit (fp, xtoi_1 (p)); } tty_fprintf (fp, "\"\n"); } } /* Print the serial number of an OpenPGP card if available. */ static void print_card_serialno (const char *serialno) { if (!serialno) return; if (opt.with_colons) return; /* Handled elsewhere. */ es_fputs (_(" Card serial no. ="), es_stdout); es_putc (' ', es_stdout); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else es_fputs (serialno, es_stdout); es_putc ('\n', es_stdout); } /* Print a public or secret (sub)key line. Example: * * pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B367 * * Some global options may result in a different output format. If * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and * depending on the value a flag character is shown: * * 1 := ' ' Regular secret key * 2 := '#' Stub secret key * 3 := '>' Secret key is on a token. */ void print_key_line (estream_t fp, PKT_public_key *pk, int secret) { char pkstrbuf[PUBKEY_STRING_SIZE]; tty_fprintf (fp, "%s%c %s", pk->flags.primary? (secret? "sec":"pub") /**/ : (secret? "ssb":"sub"), secret == 2? '#' : secret == 3? '>' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf)); if (opt.keyid_format != KF_NONE) tty_fprintf (fp, "/%s", keystr_from_pk (pk)); tty_fprintf (fp, " %s", datestr_from_pk (pk)); if ((opt.list_options & LIST_SHOW_USAGE)) { tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0)); } if (pk->flags.revoked) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->has_expired) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->expiredate) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } #if 0 /* I need to think about this some more. It's easy enough to include, but it looks sort of confusing in the listing... */ if (opt.list_options & LIST_SHOW_VALIDITY) { int validity = get_validity (ctrl, pk, NULL, NULL, 0); tty_fprintf (fp, " [%s]", trust_value_to_string (validity)); } #endif if (pk->pubkey_algo >= 100) tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo); tty_fprintf (fp, "\n"); /* if the user hasn't explicitly asked for human-readable fingerprints, show compact fpr of primary key: */ if (pk->flags.primary && !opt.fingerprint && !opt.with_fingerprint) print_fingerprint (fp, pk, 20); } void set_attrib_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; /* Fixme: Do we need to check for the log stream here? */ if (attrib_fp && attrib_fp != log_get_stream ()) es_fclose (attrib_fp); attrib_fp = NULL; if (fd == -1) return; #ifdef HAVE_DOSISH_SYSTEM setmode (fd, O_BINARY); #endif if (fd == 1) attrib_fp = es_stdout; else if (fd == 2) attrib_fp = es_stderr; else attrib_fp = es_fdopen (fd, "wb"); if (!attrib_fp) { log_fatal ("can't open fd %d for attribute output: %s\n", fd, strerror (errno)); } last_fd = fd; } diff --git a/g10/mainproc.c b/g10/mainproc.c index c1819f004..30e19fe1f 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,2486 +1,2491 @@ /* mainproc.c - handle packets * Copyright (C) 1998-2009 Free Software Foundation, Inc. * Copyright (C) 2013-2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "options.h" #include "keydb.h" #include "filter.h" #include "main.h" #include "status.h" #include "i18n.h" #include "trustdb.h" #include "keyserver-internal.h" #include "photoid.h" #include "mbox-util.h" #include "call-dirmngr.h" /* Put an upper limit on nested packets. The 32 is an arbitrary value, a much lower should actually be sufficient. */ #define MAX_NESTING_DEPTH 32 /* An object to build a list of keyid related info. */ struct kidlist_item { struct kidlist_item *next; u32 kid[2]; int pubkey_algo; int reason; }; /* * Object to hold the processing context. */ typedef struct mainproc_context *CTX; struct mainproc_context { ctrl_t ctrl; struct mainproc_context *anchor; /* May be useful in the future. */ PKT_public_key *last_pubkey; PKT_user_id *last_user_id; md_filter_context_t mfx; int sigs_only; /* Process only signatures and reject all other stuff. */ int encrypt_only; /* Process only encryption messages. */ /* Name of the file with the complete signature or the file with the detached signature. This is currently only used to deduce the file name of the data file if that has not been given. */ const char *sigfilename; /* A structure to describe the signed data in case of a detached signature. */ struct { /* A file descriptor of the the signed data. Only used if not -1. */ int data_fd; /* A list of filenames with the data files or NULL. This is only used if DATA_FD is -1. */ strlist_t data_names; /* Flag to indicated that either one of the next previous fields is used. This is only needed for better readability. */ int used; } signed_data; DEK *dek; int last_was_session_key; kbnode_t list; /* The current list of packets. */ iobuf_t iobuf; /* Used to get the filename etc. */ int trustletter; /* Temporary usage in list_node. */ ulong symkeys; struct kidlist_item *pkenc_list; /* List of encryption packets. */ struct { unsigned int sig_seen:1; /* Set to true if a signature packet has been seen. */ unsigned int data:1; /* Any data packet seen */ unsigned int uncompress_failed:1; } any; }; /*** Local prototypes. ***/ static int do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a); static void list_node (CTX c, kbnode_t node); static void proc_tree (CTX c, kbnode_t node); static int literals_seen; /*** Functions. ***/ void reset_literals_seen(void) { literals_seen = 0; } static void release_list( CTX c ) { proc_tree (c, c->list); release_kbnode (c->list); while (c->pkenc_list) { struct kidlist_item *tmp = c->pkenc_list->next; xfree (c->pkenc_list); c->pkenc_list = tmp; } c->pkenc_list = NULL; c->list = NULL; c->any.data = 0; c->any.uncompress_failed = 0; c->last_was_session_key = 0; xfree (c->dek); c->dek = NULL; } static int add_onepass_sig (CTX c, PACKET *pkt) { kbnode_t node; if (c->list) /* Add another packet. */ add_kbnode (c->list, new_kbnode (pkt)); else /* Insert the first one. */ c->list = node = new_kbnode (pkt); return 1; } static int add_gpg_control (CTX c, PACKET *pkt) { if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) { /* New clear text signature. * Process the last one and reset everything */ release_list(c); } if (c->list) /* Add another packet. */ add_kbnode (c->list, new_kbnode (pkt)); else /* Insert the first one. */ c->list = new_kbnode (pkt); return 1; } static int add_user_id (CTX c, PACKET *pkt) { if (!c->list) { log_error ("orphaned user ID\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_subkey (CTX c, PACKET *pkt) { if (!c->list) { log_error ("subkey w/o mainkey\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_ring_trust (CTX c, PACKET *pkt) { if (!c->list) { log_error ("ring trust w/o key\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_signature (CTX c, PACKET *pkt) { kbnode_t node; c->any.sig_seen = 1; if (pkt->pkttype == PKT_SIGNATURE && !c->list) { /* This is the first signature for the following datafile. * GPG does not write such packets; instead it always uses * onepass-sig packets. The drawback of PGP's method * of prepending the signature to the data is * that it is not possible to make a signature from data read * from stdin. (GPG is able to read PGP stuff anyway.) */ node = new_kbnode (pkt); c->list = node; return 1; } else if (!c->list) return 0; /* oops (invalid packet sequence)*/ else if (!c->list->pkt) BUG(); /* so nicht */ /* Add a new signature node item at the end. */ node = new_kbnode (pkt); add_kbnode (c->list, node); return 1; } static int symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen) { gcry_cipher_hd_t hd; if(slen < 17 || slen > 33) { log_error ( _("weird size for an encrypted session key (%d)\n"), (int)slen); return GPG_ERR_BAD_KEY; } if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) BUG (); if (gcry_cipher_setkey ( hd, dek->key, dek->keylen )) BUG (); gcry_cipher_setiv ( hd, NULL, 0 ); gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 ); gcry_cipher_close ( hd ); /* Now we replace the dek components with the real session key to decrypt the contents of the sequencing packet. */ dek->keylen=slen-1; dek->algo=seskey[0]; if(dek->keylen > DIM(dek->key)) BUG (); memcpy(dek->key, seskey + 1, dek->keylen); /*log_hexdump( "thekey", dek->key, dek->keylen );*/ return 0; } static void proc_symkey_enc (CTX c, PACKET *pkt) { PKT_symkey_enc *enc; enc = pkt->pkt.symkey_enc; if (!enc) log_error ("invalid symkey encrypted packet\n"); else if(!c->dek) { int algo = enc->cipher_algo; const char *s = openpgp_cipher_algo_name (algo); if (!openpgp_cipher_test_algo (algo)) { if (!opt.quiet) { if (enc->seskeylen) log_info (_("%s encrypted session key\n"), s ); else log_info (_("%s encrypted data\n"), s ); } } else log_error (_("encrypted with unknown algorithm %d\n"), algo); if (openpgp_md_test_algo (enc->s2k.hash_algo)) { log_error(_("passphrase generated with unknown digest" " algorithm %d\n"),enc->s2k.hash_algo); s = NULL; } c->last_was_session_key = 2; if (!s || opt.list_only) goto leave; if (opt.override_session_key) { c->dek = xmalloc_clear (sizeof *c->dek); if (get_override_session_key (c->dek, opt.override_session_key)) { xfree (c->dek); c->dek = NULL; } } else { c->dek = passphrase_to_dek (algo, &enc->s2k, 0, 0, NULL, NULL); if (c->dek) { c->dek->symmetric = 1; /* FIXME: This doesn't work perfectly if a symmetric key comes before a public key in the message - if the user doesn't know the passphrase, then there is a chance that the "decrypted" algorithm will happen to be a valid one, which will make the returned dek appear valid, so we won't try any public keys that come later. */ if (enc->seskeylen) { if (symkey_decrypt_seskey (c->dek, enc->seskey, enc->seskeylen)) { xfree (c->dek); c->dek = NULL; } } else c->dek->algo_info_printed = 1; } } } leave: c->symkeys++; free_packet (pkt); } static void proc_pubkey_enc (ctrl_t ctrl, CTX c, PACKET *pkt) { PKT_pubkey_enc *enc; int result = 0; /* Check whether the secret key is available and store in this case. */ c->last_was_session_key = 1; enc = pkt->pkt.pubkey_enc; /*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/ /* Hmmm: why do I have this algo check here - anyway there is * function to check it. */ if (opt.verbose) log_info (_("public key is %s\n"), keystr (enc->keyid)); if (is_status_enabled()) { char buf[50]; /* FIXME: For ECC support we need to map the OpenPGP algo number to the Libgcrypt defined one. This is due a chicken-egg problem: We need to have code in Libgcrypt for a new algorithm so to implement a proposed new algorithm before the IANA will finally assign an OpenPGP identifier. */ snprintf (buf, sizeof buf, "%08lX%08lX %d 0", (ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo); write_status_text (STATUS_ENC_TO, buf); } if (!opt.list_only && opt.override_session_key) { /* It does not make much sense to store the session key in * secure memory because it has already been passed on the * command line and the GCHQ knows about it. */ c->dek = xmalloc_clear (sizeof *c->dek); result = get_override_session_key (c->dek, opt.override_session_key); if (result) { xfree (c->dek); c->dek = NULL; } } else if (enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E || enc->pubkey_algo == PUBKEY_ALGO_ECDH || enc->pubkey_algo == PUBKEY_ALGO_RSA || enc->pubkey_algo == PUBKEY_ALGO_RSA_E || enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL) { /* Note that we also allow type 20 Elgamal keys for decryption. There are still a couple of those keys in active use as a subkey. */ /* FIXME: Store this all in a list and process it later so that we can prioritize what key to use. This gives a better user experience if wildcard keyids are used. */ if (!c->dek && ((!enc->keyid[0] && !enc->keyid[1]) || opt.try_all_secrets || have_secret_key_with_kid (enc->keyid))) { if(opt.list_only) result = -1; else { c->dek = xmalloc_secure_clear (sizeof *c->dek); if ((result = get_session_key (ctrl, enc, c->dek))) { /* Error: Delete the DEK. */ xfree (c->dek); c->dek = NULL; } } } else result = GPG_ERR_NO_SECKEY; } else result = GPG_ERR_PUBKEY_ALGO; if (result == -1) ; else { /* Store it for later display. */ struct kidlist_item *x = xmalloc (sizeof *x); x->kid[0] = enc->keyid[0]; x->kid[1] = enc->keyid[1]; x->pubkey_algo = enc->pubkey_algo; x->reason = result; x->next = c->pkenc_list; c->pkenc_list = x; if (!result && opt.verbose > 1) log_info (_("public key encrypted data: good DEK\n")); } free_packet(pkt); } /* * Print the list of public key encrypted packets which we could * not decrypt. */ static void print_pkenc_list (struct kidlist_item *list, int failed) { for (; list; list = list->next) { PKT_public_key *pk; const char *algstr; if (failed && !list->reason) continue; if (!failed && list->reason) continue; algstr = openpgp_pk_algo_name (list->pubkey_algo); pk = xmalloc_clear (sizeof *pk); if (!algstr) algstr = "[?]"; pk->pubkey_algo = list->pubkey_algo; if (!get_pubkey (pk, list->kid)) { char *p; log_info (_("encrypted with %u-bit %s key, ID %s, created %s\n"), nbits_from_pk (pk), algstr, keystr_from_pk(pk), strtimestamp (pk->timestamp)); p = get_user_id_native (list->kid); log_printf (_(" \"%s\"\n"), p); xfree (p); } else log_info (_("encrypted with %s key, ID %s\n"), algstr, keystr(list->kid)); free_public_key (pk); if (gpg_err_code (list->reason) == GPG_ERR_NO_SECKEY) { if (is_status_enabled()) { char buf[20]; snprintf (buf, sizeof buf, "%08lX%08lX", (ulong)list->kid[0], (ulong)list->kid[1]); write_status_text (STATUS_NO_SECKEY, buf); } } else if (list->reason) { log_info (_("public key decryption failed: %s\n"), gpg_strerror (list->reason)); write_status_error ("pkdecrypt_failed", list->reason); } } } static void proc_encrypted (CTX c, PACKET *pkt) { int result = 0; if (!opt.quiet) { if (c->symkeys>1) log_info (_("encrypted with %lu passphrases\n"), c->symkeys); else if (c->symkeys == 1) log_info (_("encrypted with 1 passphrase\n")); print_pkenc_list ( c->pkenc_list, 1 ); print_pkenc_list ( c->pkenc_list, 0 ); } /* FIXME: Figure out the session key by looking at all pkenc packets. */ write_status (STATUS_BEGIN_DECRYPTION); /*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/ if (opt.list_only) result = -1; else if (!c->dek && !c->last_was_session_key) { int algo; STRING2KEY s2kbuf; STRING2KEY *s2k = NULL; int canceled; if (opt.override_session_key) { c->dek = xmalloc_clear (sizeof *c->dek); result = get_override_session_key (c->dek, opt.override_session_key); if (result) { xfree (c->dek); c->dek = NULL; } } else { /* Assume this is old style conventional encrypted data. */ algo = opt.def_cipher_algo; if (algo) log_info (_("assuming %s encrypted data\n"), openpgp_cipher_algo_name (algo)); else if (openpgp_cipher_test_algo (CIPHER_ALGO_IDEA)) { algo = opt.def_cipher_algo; if (!algo) algo = opt.s2k_cipher_algo; log_info (_("IDEA cipher unavailable, " "optimistically attempting to use %s instead\n"), openpgp_cipher_algo_name (algo)); } else { algo = CIPHER_ALGO_IDEA; if (!opt.s2k_digest_algo) { /* If no digest is given we assume SHA-1. */ s2kbuf.mode = 0; s2kbuf.hash_algo = DIGEST_ALGO_SHA1; s2k = &s2kbuf; } log_info (_("assuming %s encrypted data\n"), "IDEA"); } c->dek = passphrase_to_dek (algo, s2k, 0, 0, NULL, &canceled); if (c->dek) c->dek->algo_info_printed = 1; else if (canceled) result = gpg_error (GPG_ERR_CANCELED); else result = gpg_error (GPG_ERR_INV_PASSPHRASE); } } else if (!c->dek) result = GPG_ERR_NO_SECKEY; if (!result) result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek ); if (result == -1) ; else if (!result && !opt.ignore_mdc_error && !pkt->pkt.encrypted->mdc_method && openpgp_cipher_get_algo_blklen (c->dek->algo) != 8 && c->dek->algo != CIPHER_ALGO_TWOFISH) { /* The message has been decrypted but has no MDC despite that a modern cipher (blocklength != 64 bit, except for Twofish) is used and the option to ignore MDC errors is not used: To avoid attacks changing an MDC message to a non-MDC message, we fail here. */ log_error (_("WARNING: message was not integrity protected\n")); if (opt.verbose > 1) log_info ("decryption forced to fail\n"); write_status (STATUS_DECRYPTION_FAILED); } else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE && opt.ignore_mdc_error)) { write_status (STATUS_DECRYPTION_OKAY); if (opt.verbose > 1) log_info(_("decryption okay\n")); if (pkt->pkt.encrypted->mdc_method && !result) write_status (STATUS_GOODMDC); else if (!opt.no_mdc_warn) log_info (_("WARNING: message was not integrity protected\n")); } else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE) { glo_ctrl.lasterr = result; log_error (_("WARNING: encrypted message has been manipulated!\n")); write_status (STATUS_BADMDC); write_status (STATUS_DECRYPTION_FAILED); } else { if (gpg_err_code (result) == GPG_ERR_BAD_KEY && *c->dek->s2k_cacheid != '\0') { if (opt.debug) log_debug ("cleared passphrase cached with ID: %s\n", c->dek->s2k_cacheid); passphrase_clear_cache (c->dek->s2k_cacheid); } glo_ctrl.lasterr = result; write_status (STATUS_DECRYPTION_FAILED); log_error (_("decryption failed: %s\n"), gpg_strerror (result)); /* Hmmm: does this work when we have encrypted using multiple * ways to specify the session key (symmmetric and PK). */ } xfree (c->dek); c->dek = NULL; free_packet (pkt); c->last_was_session_key = 0; write_status (STATUS_END_DECRYPTION); } static void proc_plaintext( CTX c, PACKET *pkt ) { PKT_plaintext *pt = pkt->pkt.plaintext; int any, clearsig, rc; kbnode_t n; literals_seen++; if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) log_info (_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context (&c->mfx); if (gcry_md_open (&c->mfx.md, 0, 0)) BUG (); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested * Hmmm, why don't we need it at all if we have sigclass 1 * Should we assume that plaintext in mode 't' has always sigclass 1?? * See: Russ Allbery's mail 1999-02-09 */ any = clearsig = 0; for (n=c->list; n; n = n->next ) { if (n->pkt->pkttype == PKT_ONEPASS_SIG) { /* The onepass signature case. */ if (n->pkt->pkt.onepass_sig->digest_algo) { gcry_md_enable (c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo); any = 1; } } else if (n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START) { /* The clearsigned message case. */ size_t datalen = n->pkt->pkt.gpg_control->datalen; const byte *data = n->pkt->pkt.gpg_control->data; /* Check that we have at least the sigclass and one hash. */ if (datalen < 2) log_fatal ("invalid control packet CTRLPKT_CLEARSIGN_START\n"); /* Note that we don't set the clearsig flag for not-dash-escaped * documents. */ clearsig = (*data == 0x01); for (data++, datalen--; datalen; datalen--, data++) gcry_md_enable (c->mfx.md, *data); any = 1; break; /* Stop here as one-pass signature packets are not expected. */ } else if (n->pkt->pkttype == PKT_SIGNATURE) { /* The SIG+LITERAL case that PGP used to use. */ gcry_md_enable ( c->mfx.md, n->pkt->pkt.signature->digest_algo ); any = 1; } } if (!any && !opt.skip_verify) { /* This is for the old GPG LITERAL+SIG case. It's not legal according to 2440, so hopefully it won't come up that often. There is no good way to specify what algorithms to use in that case, so these there are the historical answer. */ gcry_md_enable (c->mfx.md, DIGEST_ALGO_RMD160); gcry_md_enable (c->mfx.md, DIGEST_ALGO_SHA1); } if (DBG_HASHING) { gcry_md_debug (c->mfx.md, "verify"); if (c->mfx.md2) gcry_md_debug (c->mfx.md2, "verify2"); } rc=0; if (literals_seen > 1) { log_info (_("WARNING: multiple plaintexts seen\n")); if (!opt.flags.allow_multiple_messages) { write_status_text (STATUS_ERROR, "proc_pkt.plaintext 89_BAD_DATA"); log_inc_errorcount (); rc = gpg_error (GPG_ERR_UNEXPECTED); } } if (!rc) { /* It we are in --verify mode, we do not want to output the * signed text. However, if --output is also used we do what * has been requested and write out the signed data. */ rc = handle_plaintext (pt, &c->mfx, (opt.outfp || opt.outfile)? 0 : c->sigs_only, clearsig); if (gpg_err_code (rc) == GPG_ERR_EACCES && !c->sigs_only) { /* Can't write output but we hash it anyway to check the signature. */ rc = handle_plaintext( pt, &c->mfx, 1, clearsig ); } } if (rc) log_error ("handle plaintext failed: %s\n", gpg_strerror (rc)); free_packet(pkt); c->last_was_session_key = 0; /* We add a marker control packet instead of the plaintext packet. * This is so that we can later detect invalid packet sequences. */ n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK, NULL, 0)); if (c->list) add_kbnode (c->list, n); else c->list = n; } static int proc_compressed_cb (iobuf_t a, void *info) { if ( ((CTX)info)->signed_data.used && ((CTX)info)->signed_data.data_fd != -1) return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_fd); else return proc_signature_packets (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_names, ((CTX)info)->sigfilename ); } static int proc_encrypt_cb (iobuf_t a, void *info ) { CTX c = info; return proc_encryption_packets (c->ctrl, info, a ); } static int proc_compressed (CTX c, PACKET *pkt) { PKT_compressed *zd = pkt->pkt.compressed; int rc; /*printf("zip: compressed data packet\n");*/ if (c->sigs_only) rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c); else if( c->encrypt_only ) rc = handle_compressed (c->ctrl, c, zd, proc_encrypt_cb, c); else rc = handle_compressed (c->ctrl, c, zd, NULL, NULL); if (gpg_err_code (rc) == GPG_ERR_BAD_DATA) { if (!c->any.uncompress_failed) { CTX cc; for (cc=c; cc; cc = cc->anchor) cc->any.uncompress_failed = 1; log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); } } else if (rc) log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); free_packet(pkt); c->last_was_session_key = 0; return rc; } /* * Check the signature. If R_PK is not NULL a copy of the public key * used to verify the signature will be stored tehre, or NULL if not * found. Returns: 0 = valid signature or an error code */ static int do_check_sig (CTX c, kbnode_t node, int *is_selfsig, int *is_expkey, int *is_revkey, PKT_public_key **r_pk) { PKT_signature *sig; gcry_md_hd_t md = NULL; gcry_md_hd_t md2 = NULL; gcry_md_hd_t md_good = NULL; int algo, rc; if (r_pk) *r_pk = NULL; log_assert (node->pkt->pkttype == PKT_SIGNATURE); if (is_selfsig) *is_selfsig = 0; sig = node->pkt->pkt.signature; algo = sig->digest_algo; rc = openpgp_md_test_algo (algo); if (rc) return rc; if (sig->sig_class == 0x00) { if (c->mfx.md) { if (gcry_md_copy (&md, c->mfx.md )) BUG (); } else /* detached signature */ { /* check_signature() will enable the md. */ if (gcry_md_open (&md, 0, 0 )) BUG (); } } else if (sig->sig_class == 0x01) { /* How do we know that we have to hash the (already hashed) text in canonical mode ??? (calculating both modes???) */ if (c->mfx.md) { if (gcry_md_copy (&md, c->mfx.md )) BUG (); if (c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2)) BUG (); } else /* detached signature */ { log_debug ("Do we really need this here?"); /* check_signature() will enable the md*/ if (gcry_md_open (&md, 0, 0 )) BUG (); if (gcry_md_open (&md2, 0, 0 )) BUG (); } } else if ((sig->sig_class&~3) == 0x10 || sig->sig_class == 0x18 || sig->sig_class == 0x1f || sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) { if (c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY) { return check_key_signature( c->list, node, is_selfsig ); } else if (sig->sig_class == 0x20) { log_error (_("standalone revocation - " "use \"gpg --import\" to apply\n")); return GPG_ERR_NOT_PROCESSED; } else { log_error ("invalid root packet for sigclass %02x\n", sig->sig_class); return GPG_ERR_SIG_CLASS; } } else return GPG_ERR_SIG_CLASS; /* We only get here if we are checking the signature of a binary (0x00) or text document (0x01). */ rc = check_signature2 (sig, md, NULL, is_expkey, is_revkey, r_pk); if (! rc) md_good = md; else if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2) { PKT_public_key *pk2; rc = check_signature2 (sig, md2, NULL, is_expkey, is_revkey, r_pk? &pk2 : NULL); if (!rc) { md_good = md2; if (r_pk) { free_public_key (*r_pk); *r_pk = pk2; } } } if (md_good) { unsigned char *buffer = gcry_md_read (md_good, sig->digest_algo); sig->digest_len = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (algo)); memcpy (sig->digest, buffer, sig->digest_len); } gcry_md_close (md); gcry_md_close (md2); return rc; } static void print_userid (PACKET *pkt) { if (!pkt) BUG(); if (pkt->pkttype != PKT_USER_ID) { es_printf ("ERROR: unexpected packet type %d", pkt->pkttype ); return; } if (opt.with_colons) { if (pkt->pkt.user_id->attrib_data) es_printf("%u %lu", pkt->pkt.user_id->numattribs, pkt->pkt.user_id->attrib_len); else es_write_sanitized (es_stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len, ":", NULL); } else print_utf8_buffer (es_stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len ); } /* * List the keyblock in a user friendly way */ static void list_node (CTX c, kbnode_t node) { if (!node) ; else if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = node->pkt->pkt.public_key; if (opt.with_colons) { u32 keyid[2]; keyid_from_pk( pk, keyid ); if (pk->flags.primary) - c->trustletter = (opt.fast_list_mode? - 0 : get_validity_info (c->ctrl, pk, NULL)); + c->trustletter = (opt.fast_list_mode + ? 0 + : get_validity_info + (c->ctrl, + node->pkt->pkttype == PKT_PUBLIC_KEY + ? node : NULL, + pk, NULL)); es_printf ("%s:", pk->flags.primary? "pub":"sub" ); if (c->trustletter) es_putc (c->trustletter, es_stdout); es_printf (":%u:%d:%08lX%08lX:%s:%s::", nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], colon_datestr_from_pk( pk ), colon_strtime (pk->expiredate) ); if (pk->flags.primary && !opt.fast_list_mode) es_putc (get_ownertrust_info (pk), es_stdout); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } else { print_key_line (es_stdout, pk, 0); } if (opt.keyid_format == KF_NONE && !opt.with_colons) ; /* Already printed. */ else if ((pk->flags.primary && opt.fingerprint) || opt.fingerprint > 1) print_fingerprint (NULL, pk, 0); if (opt.with_colons) { if (node->next && node->next->pkt->pkttype == PKT_RING_TRUST) es_printf ("rtv:1:%u:\n", node->next->pkt->pkt.ring_trust->trustval); } if (pk->flags.primary) { int kl = opt.keyid_format == KF_NONE? 0 : keystrlen (); /* Now list all userids with their signatures. */ for (node = node->next; node; node = node->next) { if (node->pkt->pkttype == PKT_SIGNATURE) { list_node (c, node ); } else if (node->pkt->pkttype == PKT_USER_ID) { if (opt.with_colons) es_printf ("%s:::::::::", node->pkt->pkt.user_id->attrib_data?"uat":"uid"); else es_printf ("uid%*s", kl + (opt.legacy_list_mode? 9:11), "" ); print_userid (node->pkt); if (opt.with_colons) es_putc (':', es_stdout); es_putc ('\n', es_stdout); if (opt.with_colons && node->next && node->next->pkt->pkttype == PKT_RING_TRUST) { es_printf ("rtv:2:%u:\n", node->next->pkt->pkt.ring_trust? node->next->pkt->pkt.ring_trust->trustval : 0); } } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { list_node(c, node ); } } } } else if (node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { log_debug ("FIXME: No way to print secret key packets here\n"); /* fixme: We may use a function to turn a secret key packet into a public key one and use that here. */ } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int is_selfsig = 0; int rc2 = 0; size_t n; char *p; int sigrc = ' '; if (!opt.verbose) return; if (sig->sig_class == 0x20 || sig->sig_class == 0x30) es_fputs ("rev", es_stdout); else es_fputs ("sig", es_stdout); if (opt.check_sigs) { fflush (stdout); rc2 = do_check_sig (c, node, &is_selfsig, NULL, NULL, NULL); switch (gpg_err_code (rc2)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } } else /* Check whether this is a self signature. */ { u32 keyid[2]; if (c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_SECRET_KEY ) { keyid_from_pk (c->list->pkt->pkt.public_key, keyid); if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]) is_selfsig = 1; } } if (opt.with_colons) { es_putc (':', es_stdout); if (sigrc != ' ') es_putc (sigrc, es_stdout); es_printf ("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo, (ulong)sig->keyid[0], (ulong)sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (sig->trust_depth || sig->trust_value) es_printf ("%d %d",sig->trust_depth,sig->trust_value); es_putc (':', es_stdout); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_putc (':', es_stdout); } else es_printf ("%c %s %s ", sigrc, keystr (sig->keyid), datestr_from_sig(sig)); if (sigrc == '%') es_printf ("[%s] ", gpg_strerror (rc2) ); else if (sigrc == '?') ; else if (is_selfsig) { if (opt.with_colons) es_putc (':', es_stdout); es_fputs (sig->sig_class == 0x18? "[keybind]":"[selfsig]", es_stdout); if (opt.with_colons) es_putc (':', es_stdout); } else if (!opt.fast_list_mode) { p = get_user_id (sig->keyid, &n); es_write_sanitized (es_stdout, p, n, opt.with_colons?":":NULL, NULL ); xfree (p); } if (opt.with_colons) es_printf (":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l'); es_putc ('\n', es_stdout); } else log_error ("invalid node with packet of type %d\n", node->pkt->pkttype); } int proc_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) { int rc; CTX c = xmalloc_clear (sizeof *c); c->ctrl = ctrl; c->anchor = anchor; rc = do_proc_packets (ctrl, c, a); xfree (c); return rc; } int proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, strlist_t signedfiles, const char *sigfilename ) { CTX c = xmalloc_clear (sizeof *c); int rc; c->ctrl = ctrl; c->anchor = anchor; c->sigs_only = 1; c->signed_data.data_fd = -1; c->signed_data.data_names = signedfiles; c->signed_data.used = !!signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets (ctrl, c, a); /* If we have not encountered any signature we print an error messages, send a NODATA status back and return an error code. Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); rc = GPG_ERR_NO_DATA; } /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ if (!rc && c->anchor && c->any.sig_seen) c->anchor->any.sig_seen = 1; xfree (c); return rc; } int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a, int signed_data_fd ) { int rc; CTX c; c = xtrycalloc (1, sizeof *c); if (!c) return gpg_error_from_syserror (); c->ctrl = ctrl; c->anchor = anchor; c->sigs_only = 1; c->signed_data.data_fd = signed_data_fd; c->signed_data.data_names = NULL; c->signed_data.used = (signed_data_fd != -1); rc = do_proc_packets (ctrl, c, a); /* If we have not encountered any signature we print an error messages, send a NODATA status back and return an error code. Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); rc = gpg_error (GPG_ERR_NO_DATA); } /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ if (!rc && c->anchor && c->any.sig_seen) c->anchor->any.sig_seen = 1; xfree ( c ); return rc; } int proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) { CTX c = xmalloc_clear (sizeof *c); int rc; c->ctrl = ctrl; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets (ctrl, c, a); xfree (c); return rc; } static int check_nesting (CTX c) { int level; for (level=0; c; c = c->anchor) level++; if (level > MAX_NESTING_DEPTH) { log_error ("input data with too deeply nested packets\n"); write_status_text (STATUS_UNEXPECTED, "1"); return GPG_ERR_BAD_DATA; } return 0; } static int do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) { PACKET *pkt; int rc = 0; int any_data = 0; int newpkt; rc = check_nesting (c); if (rc) return rc; pkt = xmalloc( sizeof *pkt ); c->iobuf = a; init_packet(pkt); while ((rc=parse_packet(a, pkt)) != -1) { any_data = 1; if (rc) { free_packet (pkt); /* Stop processing when an invalid packet has been encountered * but don't do so when we are doing a --list-packets. */ if (gpg_err_code (rc) == GPG_ERR_INV_PACKET && opt.list_packets == 0) break; continue; } newpkt = -1; if (opt.list_packets) { switch (pkt->pkttype) { case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; default: newpkt = 0; break; } } else if (c->sigs_only) { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: case PKT_SYMKEY_ENC: case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: write_status_text( STATUS_UNEXPECTED, "0" ); rc = GPG_ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break; default: newpkt = 0; break; } } else if (c->encrypt_only) { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: write_status_text (STATUS_UNEXPECTED, "0"); rc = GPG_ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break; default: newpkt = 0; break; } } else { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: release_list (c); c->list = new_kbnode (pkt); newpkt = 1; break; case PKT_PUBLIC_SUBKEY: case PKT_SECRET_SUBKEY: newpkt = add_subkey (c, pkt); break; case PKT_USER_ID: newpkt = add_user_id (c, pkt); break; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break; case PKT_RING_TRUST: newpkt = add_ring_trust (c, pkt); break; default: newpkt = 0; break; } } if (rc) goto leave; /* This is a very ugly construct and frankly, I don't remember why * I used it. Adding the MDC check here is a hack. * The right solution is to initiate another context for encrypted * packet and not to reuse the current one ... It works right * when there is a compression packet between which adds just * an extra layer. * Hmmm: Rewrite this whole module here?? */ if (pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC) c->any.data = (pkt->pkttype == PKT_PLAINTEXT); if (newpkt == -1) ; else if (newpkt) { pkt = xmalloc (sizeof *pkt); init_packet (pkt); } else free_packet(pkt); } if (rc == GPG_ERR_INV_PACKET) write_status_text (STATUS_NODATA, "3"); if (any_data) rc = 0; else if (rc == -1) write_status_text (STATUS_NODATA, "2"); leave: release_list (c); xfree(c->dek); free_packet (pkt); xfree (pkt); free_md_filter_context (&c->mfx); return rc; } /* Helper for pka_uri_from_sig to parse the to-be-verified address out of the notation data. */ static pka_info_t * get_pka_address (PKT_signature *sig) { pka_info_t *pka = NULL; struct notation *nd,*notation; notation=sig_to_notation(sig); for(nd=notation;nd;nd=nd->next) { if(strcmp(nd->name,"pka-address@gnupg.org")!=0) continue; /* Not the notation we want. */ /* For now we only use the first valid PKA notation. In future we might want to keep additional PKA notations in a linked list. */ if (is_valid_mailbox (nd->value)) { pka = xmalloc (sizeof *pka + strlen(nd->value)); pka->valid = 0; pka->checked = 0; pka->uri = NULL; strcpy (pka->email, nd->value); break; } } free_notation(notation); return pka; } /* Return the URI from a DNS PKA record. If this record has already be retrieved for the signature we merely return it; if not we go out and try to get that DNS record. */ static const char * pka_uri_from_sig (CTX c, PKT_signature *sig) { if (!sig->flags.pka_tried) { log_assert (!sig->pka_info); sig->flags.pka_tried = 1; sig->pka_info = get_pka_address (sig); if (sig->pka_info) { char *url; unsigned char *fpr; size_t fprlen; if (!gpg_dirmngr_get_pka (c->ctrl, sig->pka_info->email, &fpr, &fprlen, &url)) { if (fpr && fprlen == sizeof sig->pka_info->fpr) { memcpy (sig->pka_info->fpr, fpr, fprlen); if (url) { sig->pka_info->valid = 1; if (!*url) xfree (url); else sig->pka_info->uri = url; url = NULL; } } xfree (fpr); xfree (url); } } } return sig->pka_info? sig->pka_info->uri : NULL; } /* Return true if the AKL has the WKD method specified. */ static int akl_has_wkd_method (void) { struct akl *akl; for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type == AKL_WKD) return 1; return 0; } /* Return the ISSUER fingerprint string in human readbale format if * available. Caller must release the string. */ static char * issuer_fpr_string (PKT_signature *sig) { const byte *p; size_t n; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_ISSUER_FPR, &n); if (p && n == 21 && p[0] == 4) return bin2hex (p+1, n-1, NULL); return NULL; } static void print_good_bad_signature (int statno, const char *keyid_str, kbnode_t un, PKT_signature *sig, int rc) { char *p; write_status_text_and_buffer (statno, keyid_str, un? un->pkt->pkt.user_id->name:"[?]", un? un->pkt->pkt.user_id->len:3, -1); if (un) p = utf8_to_native (un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len, 0); else p = xstrdup ("[?]"); if (rc) log_info (_("BAD signature from \"%s\""), p); else if (sig->flags.expired) log_info (_("Expired signature from \"%s\""), p); else log_info (_("Good signature from \"%s\""), p); xfree (p); } static int check_sig_and_print (CTX c, kbnode_t node) { PKT_signature *sig = node->pkt->pkt.signature; const char *astr; int rc; int is_expkey = 0; int is_revkey = 0; char *issuer_fpr; PKT_public_key *pk = NULL; /* The public key for the signature or NULL. */ if (opt.skip_verify) { log_info(_("signature verification suppressed\n")); return 0; } /* Check that the message composition is valid. * * Per RFC-2440bis (-15) allowed: * * S{1,n} -- detached signature. * S{1,n} P -- old style PGP2 signature * O{1,n} P S{1,n} -- standard OpenPGP signature. * C P S{1,n} -- cleartext signature. * * * O = One-Pass Signature packet. * S = Signature packet. * P = OpenPGP Message packet (Encrypted | Compressed | Literal) * (Note that the current rfc2440bis draft also allows * for a signed message but that does not work as it * introduces ambiguities.) * We keep track of these packages using the marker packet * CTRLPKT_PLAINTEXT_MARK. * C = Marker packet for cleartext signatures. * * We reject all other messages. * * Actually we are calling this too often, i.e. for verification of * each message but better have some duplicate work than to silently * introduce a bug here. */ { kbnode_t n; int n_onepass, n_sig; /* log_debug ("checking signature packet composition\n"); */ /* dump_kbnode (c->list); */ n = c->list; log_assert (n); if ( n->pkt->pkttype == PKT_SIGNATURE ) { /* This is either "S{1,n}" case (detached signature) or "S{1,n} P" (old style PGP2 signature). */ for (n = n->next; n; n = n->next) if (n->pkt->pkttype != PKT_SIGNATURE) break; if (!n) ; /* Okay, this is a detached signature. */ else if (n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK) ) { if (n->next) goto ambiguous; /* We only allow one P packet. */ } else goto ambiguous; } else if (n->pkt->pkttype == PKT_ONEPASS_SIG) { /* This is the "O{1,n} P S{1,n}" case (standard signature). */ for (n_onepass=1, n = n->next; n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next) n_onepass++; if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK))) goto ambiguous; for (n_sig=0, n = n->next; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) n_sig++; if (!n_sig) goto ambiguous; /* If we wanted to disallow multiple sig verification, we'd do something like this: if (n && !opt.allow_multisig_verification) goto ambiguous; However, now that we have --allow-multiple-messages, this can stay allowable as we can't get here unless multiple messages (i.e. multiple literals) are allowed. */ if (n_onepass != n_sig) { log_info ("number of one-pass packets does not match " "number of signature packets\n"); goto ambiguous; } } else if (n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) { /* This is the "C P S{1,n}" case (clear text signature). */ n = n->next; if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK))) goto ambiguous; for (n_sig=0, n = n->next; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) n_sig++; if (n || !n_sig) goto ambiguous; } else { ambiguous: log_error(_("can't handle this ambiguous signature data\n")); return 0; } } if (sig->signers_uid) write_status_buffer (STATUS_NEWSIG, sig->signers_uid, strlen (sig->signers_uid), 0); else write_status_text (STATUS_NEWSIG, NULL); astr = openpgp_pk_algo_name ( sig->pubkey_algo ); if ((issuer_fpr = issuer_fpr_string (sig))) { log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp)); log_info (_(" using %s key %s\n"), astr? astr: "?", issuer_fpr); xfree (issuer_fpr); } else if (!keystrlen () || keystrlen () > 8) { log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp)); log_info (_(" using %s key %s\n"), astr? astr: "?", keystr(sig->keyid)); } else /* Legacy format. */ log_info (_("Signature made %s using %s key ID %s\n"), asctimestamp(sig->timestamp), astr? astr: "?", keystr(sig->keyid)); /* In verbose mode print the signers UID. */ if (sig->signers_uid) log_info (_(" issuer \"%s\"\n"), sig->signers_uid); rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); /* If the key isn't found, check for a preferred keyserver. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && sig->flags.pref_ks) { const byte *p; int seq = 0; size_t n; while ((p=enum_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS,&n,&seq,NULL))) { /* According to my favorite copy editor, in English grammar, you say "at" if the key is located on a web page, but "from" if it is located on a keyserver. I'm not going to even try to make two strings here :) */ log_info(_("Key available at: ") ); print_utf8_buffer (log_get_stream(), p, n); log_printf ("\n"); if (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE && opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) { struct keyserver_spec *spec; spec = parse_preferred_keyserver (sig); if (spec) { int res; free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_keyid (c->ctrl, sig->keyid,spec, 1); glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); free_keyserver_spec (spec); if (!rc) break; } } } } /* If the avove methods didn't work, our next try is to use the URI * from a DNS PKA record. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && (opt.keyserver_options.options & KEYSERVER_HONOR_PKA_RECORD)) { const char *uri = pka_uri_from_sig (c, sig); if (uri) { /* FIXME: We might want to locate the key using the fingerprint instead of the keyid. */ int res; struct keyserver_spec *spec; spec = parse_keyserver_uri (uri, 1); if (spec) { free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_keyid (c->ctrl, sig->keyid, spec, 1); glo_ctrl.in_auto_key_retrieve--; free_keyserver_spec (spec); if (!res) rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); } } } /* If the above methods didn't work, our next try is to locate * the key via its fingerprint from a keyserver. This requires * that the signers fingerprint is encoded in the signature. We * favor this over the WKD method (to be tried next), because an * arbitrary keyserver is less subject to web bug like monitoring. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (c->ctrl)) { int res; const byte *p; size_t n; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_ISSUER_FPR, &n); if (p && n == 21 && p[0] == 4) { /* v4 packet with a SHA-1 fingerprint. */ free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_fprint (c->ctrl, p+1, n-1, opt.keyserver, 1); glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); } } /* If the above methods didn't work, our next try is to retrieve the * key from the WKD. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && !opt.flags.disable_signer_uid && akl_has_wkd_method () && sig->signers_uid) { int res; free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_wkd (c->ctrl, sig->signers_uid, 1, NULL, NULL); glo_ctrl.in_auto_key_retrieve--; /* Fixme: If the fingerprint is embedded in the signature, * compare it to the fingerprint of the returned key. */ if (!res) rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); } /* If the above methods did't work, our next try is to use a * keyserver. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (c->ctrl)) { int res; free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_keyid (c->ctrl, sig->keyid, opt.keyserver, 1); glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, NULL, &is_expkey, &is_revkey, &pk); } if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) { kbnode_t un, keyblock; int count = 0; int statno; char keyid_str[50]; PKT_public_key *mainpk = NULL; if (rc) statno = STATUS_BADSIG; else if (sig->flags.expired) statno = STATUS_EXPSIG; else if (is_expkey) statno = STATUS_EXPKEYSIG; else if(is_revkey) statno = STATUS_REVKEYSIG; else statno = STATUS_GOODSIG; /* FIXME: We should have the public key in PK and thus the * keyboock has already been fetched. Thus we could use the * fingerprint or PK itself to lookup the entire keyblock. That * would best be done with a cache. */ keyblock = get_pubkeyblock (sig->keyid); snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ", (ulong)sig->keyid[0], (ulong)sig->keyid[1]); /* Find and print the primary user ID along with the "Good|Expired|Bad signature" line. */ for (un=keyblock; un; un = un->next) { int valid; if (un->pkt->pkttype==PKT_PUBLIC_KEY) { mainpk = un->pkt->pkt.public_key; continue; } if (un->pkt->pkttype != PKT_USER_ID) continue; if (!un->pkt->pkt.user_id->created) continue; if (un->pkt->pkt.user_id->is_revoked) continue; if (un->pkt->pkt.user_id->is_expired) continue; if (!un->pkt->pkt.user_id->is_primary) continue; /* We want the textual primary user ID here */ if (un->pkt->pkt.user_id->attrib_data) continue; log_assert (mainpk); /* Since this is just informational, don't actually ask the user to update any trust information. (Note: we register the signature later.) Because print_good_bad_signature does not print a LF we need to compute the validity before calling that function. */ if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) - valid = get_validity (c->ctrl, mainpk, un->pkt->pkt.user_id, - NULL, 0); + valid = get_validity (c->ctrl, keyblock, mainpk, + un->pkt->pkt.user_id, NULL, 0); else valid = 0; /* Not used. */ keyid_str[17] = 0; /* cut off the "[uncertain]" part */ print_good_bad_signature (statno, keyid_str, un, sig, rc); if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) log_printf (" [%s]\n",trust_value_to_string(valid)); else log_printf ("\n"); count++; } log_assert (mainpk); /* In case we did not found a valid valid textual userid above we print the first user id packet or a "[?]" instead along with the "Good|Expired|Bad signature" line. */ if (!count) { /* Try for an invalid textual userid */ for (un=keyblock; un; un = un->next) { if (un->pkt->pkttype == PKT_USER_ID && !un->pkt->pkt.user_id->attrib_data) break; } /* Try for any userid at all */ if (!un) { for (un=keyblock; un; un = un->next) { if (un->pkt->pkttype == PKT_USER_ID) break; } } if (opt.trust_model==TM_ALWAYS || !un) keyid_str[17] = 0; /* cut off the "[uncertain]" part */ print_good_bad_signature (statno, keyid_str, un, sig, rc); if (opt.trust_model != TM_ALWAYS && un) log_printf (" %s",_("[uncertain]") ); log_printf ("\n"); } /* If we have a good signature and already printed * the primary user ID, print all the other user IDs */ if (count && !rc && !(opt.verify_options & VERIFY_SHOW_PRIMARY_UID_ONLY)) { char *p; for( un=keyblock; un; un = un->next) { if (un->pkt->pkttype != PKT_USER_ID) continue; if ((un->pkt->pkt.user_id->is_revoked || un->pkt->pkt.user_id->is_expired) && !(opt.verify_options & VERIFY_SHOW_UNUSABLE_UIDS)) continue; /* Skip textual primary user ids which we printed above. */ if (un->pkt->pkt.user_id->is_primary && !un->pkt->pkt.user_id->attrib_data ) continue; /* If this user id has attribute data, print that. */ if (un->pkt->pkt.user_id->attrib_data) { dump_attribs (un->pkt->pkt.user_id, mainpk); if (opt.verify_options&VERIFY_SHOW_PHOTOS) show_photos (c->ctrl, un->pkt->pkt.user_id->attribs, un->pkt->pkt.user_id->numattribs, mainpk ,un->pkt->pkt.user_id); } p = utf8_to_native (un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len, 0); log_info (_(" aka \"%s\""), p); xfree (p); if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) { const char *valid; if (un->pkt->pkt.user_id->is_revoked) valid = _("revoked"); else if (un->pkt->pkt.user_id->is_expired) valid = _("expired"); else /* Since this is just informational, don't actually ask the user to update any trust information. */ valid = (trust_value_to_string - (get_validity (c->ctrl, mainpk, + (get_validity (c->ctrl, keyblock, mainpk, un->pkt->pkt.user_id, NULL, 0))); log_printf (" [%s]\n",valid); } else log_printf ("\n"); } } /* For good signatures print notation data. */ if (!rc) { if ((opt.verify_options & VERIFY_SHOW_POLICY_URLS)) show_policy_url (sig, 0, 1); else show_policy_url (sig, 0, 2); if ((opt.verify_options & VERIFY_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 0, 1); else show_keyserver_url (sig, 0, 2); if ((opt.verify_options & VERIFY_SHOW_NOTATIONS)) show_notation (sig, 0, 1, (((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0) + ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0))); else show_notation (sig, 0, 2, 0); } /* For good signatures print the VALIDSIG status line. */ if (!rc && is_status_enabled () && pk) { char pkhex[MAX_FINGERPRINT_LEN*2+1]; char mainpkhex[MAX_FINGERPRINT_LEN*2+1]; hexfingerprint (pk, pkhex, sizeof pkhex); hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex); /* TODO: Replace the reserved '0' in the field below with bits for status flags (policy url, notation, etc.). */ write_status_printf (STATUS_VALIDSIG, "%s %s %lu %lu %d 0 %d %d %02X %s", pkhex, strtimestamp (sig->timestamp), (ulong)sig->timestamp, (ulong)sig->expiredate, sig->version, sig->pubkey_algo, sig->digest_algo, sig->sig_class, mainpkhex); } /* For good signatures compute and print the trust information. Note that in the Tofu trust model this may ask the user on how to resolve a conflict. */ if (!rc) { if ((opt.verify_options & VERIFY_PKA_LOOKUPS)) pka_uri_from_sig (c, sig); /* Make sure PKA info is available. */ rc = check_signatures_trust (c->ctrl, sig); } /* Print extra information about the signature. */ if (sig->flags.expired) { log_info (_("Signature expired %s\n"), asctimestamp(sig->expiredate)); rc = GPG_ERR_GENERAL; /* Need a better error here? */ } else if (sig->expiredate) log_info (_("Signature expires %s\n"), asctimestamp(sig->expiredate)); if (opt.verbose) { char pkstrbuf[PUBKEY_STRING_SIZE]; if (pk) pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); else *pkstrbuf = 0; log_info (_("%s signature, digest algorithm %s%s%s\n"), sig->sig_class==0x00?_("binary"): sig->sig_class==0x01?_("textmode"):_("unknown"), gcry_md_algo_name (sig->digest_algo), *pkstrbuf?_(", key algorithm "):"", pkstrbuf); } /* Print final warnings. */ if (!rc && !c->signed_data.used) { /* Signature is basically good but we test whether the deprecated command gpg --verify FILE.sig was used instead of gpg --verify FILE.sig FILE to verify a detached signature. If we figure out that a data file with a matching name exists, we print a warning. The problem is that the first form would also verify a standard signature. This behavior could be used to create a made up .sig file for a tarball by creating a standard signature from a valid detached signature packet (for example from a signed git tag). Then replace the sig file on the FTP server along with a changed tarball. Using the first form the verify command would correctly verify the signature but don't even consider the tarball. */ kbnode_t n; char *dfile; dfile = get_matching_datafile (c->sigfilename); if (dfile) { for (n = c->list; n; n = n->next) if (n->pkt->pkttype != PKT_SIGNATURE) break; if (n) { /* Not only signature packets in the tree thus this is not a detached signature. */ log_info (_("WARNING: not a detached signature; " "file '%s' was NOT verified!\n"), dfile); } xfree (dfile); } } free_public_key (pk); pk = NULL; release_kbnode( keyblock ); if (rc) g10_errors_seen = 1; if (opt.batch && rc) g10_exit (1); } else { char buf[50]; snprintf (buf, sizeof buf, "%08lX%08lX %d %d %02x %lu %d", (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, rc); write_status_text (STATUS_ERRSIG, buf); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) { buf[16] = 0; write_status_text (STATUS_NO_PUBKEY, buf); } if (gpg_err_code (rc) != GPG_ERR_NOT_PROCESSED) log_error (_("Can't check signature: %s\n"), gpg_strerror (rc)); } return rc; } /* * Process the tree which starts at node */ static void proc_tree (CTX c, kbnode_t node) { kbnode_t n1; int rc; if (opt.list_packets || opt.list_only) return; /* We must skip our special plaintext marker packets here because they may be the root packet. These packets are only used in additional checks and skipping them here doesn't matter. */ while (node && node->pkt->pkttype == PKT_GPG_CONTROL && node->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK) { node = node->next; } if (!node) return; c->trustletter = ' '; if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { merge_keys_and_selfsig (node); list_node (c, node); } else if (node->pkt->pkttype == PKT_SECRET_KEY) { merge_keys_and_selfsig (node); list_node (c, node); } else if (node->pkt->pkttype == PKT_ONEPASS_SIG) { /* Check all signatures. */ if (!c->any.data) { int use_textmode = 0; free_md_filter_context (&c->mfx); /* Prepare to create all requested message digests. */ rc = gcry_md_open (&c->mfx.md, 0, 0); if (rc) goto hash_err; /* Fixme: why looking for the signature packet and not the one-pass packet? */ for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) gcry_md_enable (c->mfx.md, n1->pkt->pkt.signature->digest_algo); if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01) use_textmode = 1; /* Ask for file and hash it. */ if (c->sigs_only) { if (c->signed_data.used && c->signed_data.data_fd != -1) rc = hash_datafile_by_fd (c->mfx.md, NULL, c->signed_data.data_fd, use_textmode); else rc = hash_datafiles (c->mfx.md, NULL, c->signed_data.data_names, c->sigfilename, use_textmode); } else { rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2, iobuf_get_real_fname (c->iobuf), use_textmode); } hash_err: if (rc) { log_error ("can't hash datafile: %s\n", gpg_strerror (rc)); return; } } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) check_sig_and_print (c, n1); } else if (node->pkt->pkttype == PKT_GPG_CONTROL && node->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START) { /* Clear text signed message. */ if (!c->any.data) { log_error ("cleartext signature without data\n"); return; } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) check_sig_and_print (c, n1); } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int multiple_ok = 1; n1 = find_next_kbnode (node, PKT_SIGNATURE); if (n1) { byte class = sig->sig_class; byte hash = sig->digest_algo; for (; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) { /* We can't currently handle multiple signatures of * different classes (we'd pretty much have to run a * different hash context for each), but if they are all * the same and it is detached signature, we make an * exception. Note that the old code also disallowed * multiple signatures if the digest algorithms are * different. We softened this restriction only for * detached signatures, to be on the safe side. */ if (n1->pkt->pkt.signature->sig_class != class || (c->any.data && n1->pkt->pkt.signature->digest_algo != hash)) { multiple_ok = 0; log_info (_("WARNING: multiple signatures detected. " "Only the first will be checked.\n")); break; } } } if (sig->sig_class != 0x00 && sig->sig_class != 0x01) { log_info(_("standalone signature of class 0x%02x\n"), sig->sig_class); } else if (!c->any.data) { /* Detached signature */ free_md_filter_context (&c->mfx); rc = gcry_md_open (&c->mfx.md, sig->digest_algo, 0); if (rc) goto detached_hash_err; if (multiple_ok) { /* If we have and want to handle multiple signatures we * need to enable all hash algorithms for the context. */ for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE)); ) if (!openpgp_md_test_algo (n1->pkt->pkt.signature->digest_algo)) gcry_md_enable (c->mfx.md, map_md_openpgp_to_gcry (n1->pkt->pkt.signature->digest_algo)); } if (RFC2440 || RFC4880) ; /* Strict RFC mode. */ else if (sig->digest_algo == DIGEST_ALGO_SHA1 && sig->pubkey_algo == PUBKEY_ALGO_DSA && sig->sig_class == 0x01) { /* Enable a workaround for a pgp5 bug when the detached * signature has been created in textmode. Note that we * do not implement this for multiple signatures with * different hash algorithms. */ rc = gcry_md_open (&c->mfx.md2, sig->digest_algo, 0); if (rc) goto detached_hash_err; } /* Here we used to have another hack to work around a pgp * 2 bug: It worked by not using the textmode for detached * signatures; this would let the first signature check * (on md) fail but the second one (on md2), which adds an * extra CR would then have produced the "correct" hash. * This is very, very ugly hack but it may haved help in * some cases (and break others). * c->mfx.md2? 0 :(sig->sig_class == 0x01) */ if (DBG_HASHING) { gcry_md_debug (c->mfx.md, "verify"); if (c->mfx.md2) gcry_md_debug (c->mfx.md2, "verify2"); } if (c->sigs_only) { if (c->signed_data.used && c->signed_data.data_fd != -1) rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2, c->signed_data.data_fd, (sig->sig_class == 0x01)); else rc = hash_datafiles (c->mfx.md, c->mfx.md2, c->signed_data.data_names, c->sigfilename, (sig->sig_class == 0x01)); } else { rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2, iobuf_get_real_fname(c->iobuf), (sig->sig_class == 0x01)); } detached_hash_err: if (rc) { log_error ("can't hash datafile: %s\n", gpg_strerror (rc)); return; } } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } else if (!opt.quiet) log_info (_("old style (PGP 2.x) signature\n")); if (multiple_ok) { for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) check_sig_and_print (c, n1); } else check_sig_and_print (c, node); } else { dump_kbnode (c->list); log_error ("invalid root packet detected in proc_tree()\n"); dump_kbnode (node); } } diff --git a/g10/photoid.c b/g10/photoid.c index b61ed1bcd..8b193b3d4 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -1,382 +1,382 @@ /* photoid.c - photo ID handling code * Copyright (C) 2001, 2002, 2005, 2006, 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #ifdef _WIN32 # ifdef HAVE_WINSOCK2_H # include # endif # include # ifndef VER_PLATFORM_WIN32_WINDOWS # define VER_PLATFORM_WIN32_WINDOWS 1 # endif #endif #include "gpg.h" #include "util.h" #include "packet.h" #include "status.h" #include "exec.h" #include "keydb.h" #include "i18n.h" #include "iobuf.h" #include "options.h" #include "main.h" #include "photoid.h" #include "ttyio.h" #include "trustdb.h" /* Generate a new photo id packet, or return NULL if canceled. FIXME: Should we add a duplicates check similar to generate_user_id? */ PKT_user_id * generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name) { PKT_user_id *uid; int error=1,i; unsigned int len; char *filename; byte *photo=NULL; byte header[16]; IOBUF file; int overflow; header[0]=0x10; /* little side of photo header length */ header[1]=0; /* big side of photo header length */ header[2]=1; /* 1 == version of photo header */ header[3]=1; /* 1 == JPEG */ for(i=4;i<16;i++) /* The reserved bytes */ header[i]=0; #define EXTRA_UID_NAME_SPACE 71 uid=xmalloc_clear(sizeof(*uid)+71); if(photo_name && *photo_name) filename=make_filename(photo_name,(void *)NULL); else { tty_printf(_("\nPick an image to use for your photo ID." " The image must be a JPEG file.\n" "Remember that the image is stored within your public key." " If you use a\n" "very large picture, your key will become very large" " as well!\n" "Keeping the image close to 240x288 is a good size" " to use.\n")); filename=NULL; } while(photo==NULL) { if(filename==NULL) { char *tempname; tty_printf("\n"); tty_enable_completion(NULL); tempname=cpr_get("photoid.jpeg.add", _("Enter JPEG filename for photo ID: ")); tty_disable_completion(); filename=make_filename(tempname,(void *)NULL); xfree(tempname); if(strlen(filename)==0) goto scram; } file=iobuf_open(filename); if (file && is_secured_file (iobuf_get_fd (file))) { iobuf_close (file); file = NULL; gpg_err_set_errno (EPERM); } if(!file) { log_error(_("unable to open JPEG file '%s': %s\n"), filename,strerror(errno)); xfree(filename); filename=NULL; continue; } len=iobuf_get_filelength(file, &overflow); if(len>6144 || overflow) { tty_printf( _("This JPEG is really large (%d bytes) !\n"),len); if(!cpr_get_answer_is_yes("photoid.jpeg.size", _("Are you sure you want to use it? (y/N) "))) { iobuf_close(file); xfree(filename); filename=NULL; continue; } } photo=xmalloc(len); iobuf_read(file,photo,len); iobuf_close(file); /* Is it a JPEG? */ if(photo[0]!=0xFF || photo[1]!=0xD8) { log_error(_("'%s' is not a JPEG file\n"),filename); xfree(photo); photo=NULL; xfree(filename); filename=NULL; continue; } /* Build the packet */ build_attribute_subpkt(uid,1,photo,len,header,16); parse_attribute_subpkts(uid); make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE); /* Showing the photo is not safe when noninteractive since the "user" may not be able to dismiss a viewer window! */ if(opt.command_fd==-1) { show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay", _("Is this photo correct (y/N/q)? "))) { case -1: goto scram; case 0: free_attributes(uid); xfree(photo); photo=NULL; xfree(filename); filename=NULL; continue; } } } error=0; uid->ref=1; scram: xfree(filename); xfree(photo); if(error) { free_attributes(uid); xfree(uid); return NULL; } return uid; } /* Returns 0 for error, 1 for valid */ int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len) { u16 headerlen; if(attr->len<3) return 0; /* For historical reasons (i.e. "oops!"), the header length is little endian. */ headerlen=(attr->data[1]<<8) | attr->data[0]; if(headerlen>attr->len) return 0; if(type && attr->len>=4) { if(attr->data[2]==1) /* header version 1 */ *type=attr->data[3]; else *type=0; } *len=attr->len-headerlen; if(*len==0) return 0; return 1; } /* style==0 for extension, 1 for name, 2 for MIME type. Remember that the "name" style string could be used in a user ID name field, so make sure it is not too big (see parse-packet.c:parse_attribute). Extensions should be 3 characters long for the best cross-platform compatibility. */ char *image_type_to_string(byte type,int style) { char *string; switch(type) { case 1: /* jpeg */ if(style==0) string="jpg"; else if(style==1) string="jpeg"; else string="image/jpeg"; break; default: if(style==0) string="bin"; else if(style==1) string="unknown"; else string="image/x-unknown"; break; } return string; } #if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER) static const char *get_default_photo_command(void) { #if defined(_WIN32) OSVERSIONINFO osvi; memset(&osvi,0,sizeof(osvi)); osvi.dwOSVersionInfoSize=sizeof(osvi); GetVersionEx(&osvi); if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) return "start /w %i"; else return "cmd /c start /w %i"; #elif defined(__APPLE__) /* OS X. This really needs more than just __APPLE__. */ return "open %I"; #elif defined(__riscos__) return "Filer_Run %I"; #else return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; #endif } #endif void show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count, PKT_public_key *pk, PKT_user_id *uid) { #ifdef DISABLE_PHOTO_VIEWER (void)attrs; (void)count; (void)pk; (void)uid; #else /*!DISABLE_PHOTO_VIEWER*/ int i; struct expando_args args; u32 len; u32 kid[2]={0,0}; memset (&args, 0, sizeof(args)); args.pk = pk; - args.validity_info = get_validity_info (ctrl, pk, uid); + args.validity_info = get_validity_info (ctrl, NULL, pk, uid); args.validity_string = get_validity_string (ctrl, pk, uid); namehash_from_uid (uid); args.namehash = uid->namehash; if (pk) keyid_from_pk (pk, kid); for(i=0;itempfile_in, image_type_to_string(args.imagetype,2)); #endif xfree(name); fwrite(&attrs[i].data[offset],attrs[i].len-offset,1,spawn->tochild); if(exec_read(spawn)!=0) { exec_finish(spawn); goto fail; } if(exec_finish(spawn)!=0) goto fail; } return; fail: log_error(_("unable to display photo ID!\n")); #endif /*!DISABLE_PHOTO_VIEWER*/ } diff --git a/g10/pkclist.c b/g10/pkclist.c index 51e8f274c..0426da890 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1,1693 +1,1694 @@ /* pkclist.c - create a list of public keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "keydb.h" #include "util.h" #include "main.h" #include "trustdb.h" #include "ttyio.h" #include "status.h" #include "photoid.h" #include "i18n.h" #include "tofu.h" #define CONTROL_D ('D' - 'A' + 1) static void send_status_inv_recp (int reason, const char *name) { char buf[40]; snprintf (buf, sizeof buf, "%d ", reason); write_status_text_and_buffer (STATUS_INV_RECP, buf, name, strlen (name), -1); } /**************** * Show the revocation reason as it is stored with the given signature */ static void do_show_revocation_reason( PKT_signature *sig ) { size_t n, nn; const byte *p, *pp; int seq = 0; const char *text; while( (p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON, &n, &seq, NULL )) ) { if( !n ) continue; /* invalid - just skip it */ if( *p == 0 ) text = _("No reason specified"); else if( *p == 0x01 ) text = _("Key is superseded"); else if( *p == 0x02 ) text = _("Key has been compromised"); else if( *p == 0x03 ) text = _("Key is no longer used"); else if( *p == 0x20 ) text = _("User ID is no longer valid"); else text = NULL; log_info ( _("reason for revocation: ")); if (text) log_printf ("%s\n", text); else log_printf ("code=%02x\n", *p ); n--; p++; pp = NULL; do { /* We don't want any empty lines, so skip them */ while( n && *p == '\n' ) { p++; n--; } if( n ) { pp = memchr( p, '\n', n ); nn = pp? pp - p : n; log_info ( _("revocation comment: ") ); es_write_sanitized (log_get_stream(), p, nn, NULL, NULL); log_printf ("\n"); p += nn; n -= nn; } } while( pp ); } } /* Mode 0: try and find the revocation based on the pk (i.e. check subkeys, etc.) Mode 1: use only the revocation on the main pk */ void show_revocation_reason( PKT_public_key *pk, int mode ) { /* Hmmm, this is not so easy because we have to duplicate the code * used in the trustbd to calculate the keyflags. We need to find * a clean way to check revocation certificates on keys and * signatures. And there should be no duplicate code. Because we * enter this function only when the trustdb told us that we have * a revoked key, we could simply look for a revocation cert and * display this one, when there is only one. Let's try to do this * until we have a better solution. */ KBNODE node, keyblock = NULL; byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; int rc; /* get the keyblock */ fingerprint_from_pk( pk, fingerprint, &fingerlen ); rc = get_pubkey_byfprint(NULL, &keyblock, fingerprint, fingerlen); if( rc ) { /* that should never happen */ log_debug( "failed to get the keyblock\n"); return; } for( node=keyblock; node; node = node->next ) { if( (mode && node->pkt->pkttype == PKT_PUBLIC_KEY) || ( ( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) && !cmp_public_keys( node->pkt->pkt.public_key, pk ) ) ) break; } if( !node ) { log_debug("Oops, PK not in keyblock\n"); release_kbnode( keyblock ); return; } /* now find the revocation certificate */ for( node = node->next; node ; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; if( node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class == 0x20 || node->pkt->pkt.signature->sig_class == 0x28 ) ) { /* FIXME: we should check the signature here */ do_show_revocation_reason ( node->pkt->pkt.signature ); break; } } /* We didn't find it, so check if the whole key is revoked */ if(!node && !mode) show_revocation_reason(pk,1); release_kbnode( keyblock ); } /**************** * mode: 0 = standard * 1 = Without key info and additional menu option 'm' * this does also add an option to set the key to ultimately trusted. * Returns: * -2 = nothing changed - caller should show some additional info * -1 = quit operation * 0 = nothing changed * 1 = new ownertrust now in new_trust */ #ifndef NO_TRUST_MODELS static int do_edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode, unsigned *new_trust, int defer_help ) { char *p; u32 keyid[2]; int changed=0; int quit=0; int show=0; int min_num; int did_help=defer_help; unsigned int minimum = tdb_get_min_ownertrust (pk); switch(minimum) { default: case TRUST_UNDEFINED: min_num=1; break; case TRUST_NEVER: min_num=2; break; case TRUST_MARGINAL: min_num=3; break; case TRUST_FULLY: min_num=4; break; } keyid_from_pk (pk, keyid); for(;;) { /* A string with valid answers. TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching strings which should be translated accordingly and the letter changed to match the one in the answer string. i = please show me more information m = back to the main menu s = skip this key q = quit */ const char *ans = _("iImMqQsS"); if( !did_help ) { if( !mode ) { KBNODE keyblock, un; tty_printf (_("No trust value assigned to:\n")); print_key_line (NULL, pk, 0); p = get_user_id_native(keyid); tty_printf (_(" \"%s\"\n"),p); xfree (p); keyblock = get_pubkeyblock (keyid); if (!keyblock) BUG (); for (un=keyblock; un; un = un->next) { if (un->pkt->pkttype != PKT_USER_ID ) continue; if (un->pkt->pkt.user_id->is_revoked ) continue; if (un->pkt->pkt.user_id->is_expired ) continue; /* Only skip textual primaries */ if (un->pkt->pkt.user_id->is_primary && !un->pkt->pkt.user_id->attrib_data ) continue; if((opt.verify_options&VERIFY_SHOW_PHOTOS) && un->pkt->pkt.user_id->attrib_data) show_photos (ctrl, un->pkt->pkt.user_id->attribs, un->pkt->pkt.user_id->numattribs, pk, un->pkt->pkt.user_id); p=utf8_to_native(un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len,0); tty_printf(_(" aka \"%s\"\n"),p); } print_fingerprint (NULL, pk, 2); tty_printf("\n"); release_kbnode (keyblock); } if(opt.trust_model==TM_DIRECT) { tty_printf(_("How much do you trust that this key actually " "belongs to the named user?\n")); tty_printf("\n"); } else { /* This string also used in keyedit.c:trustsig_prompt */ tty_printf(_("Please decide how far you trust this user to" " correctly verify other users' keys\n" "(by looking at passports, checking fingerprints from" " different sources, etc.)\n")); tty_printf("\n"); } if(min_num<=1) tty_printf (_(" %d = I don't know or won't say\n"), 1); if(min_num<=2) tty_printf (_(" %d = I do NOT trust\n"), 2); if(min_num<=3) tty_printf (_(" %d = I trust marginally\n"), 3); if(min_num<=4) tty_printf (_(" %d = I trust fully\n"), 4); if (mode) tty_printf (_(" %d = I trust ultimately\n"), 5); #if 0 /* not yet implemented */ tty_printf (" i = please show me more information\n"); #endif if( mode ) tty_printf(_(" m = back to the main menu\n")); else { tty_printf(_(" s = skip this key\n")); tty_printf(_(" q = quit\n")); } tty_printf("\n"); if(minimum) tty_printf(_("The minimum trust level for this key is: %s\n\n"), trust_value_to_string(minimum)); did_help = 1; } if( strlen(ans) != 8 ) BUG(); p = cpr_get("edit_ownertrust.value",_("Your decision? ")); trim_spaces(p); cpr_kill_prompt(); if( !*p ) did_help = 0; else if( *p && p[1] ) ; else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) ) { unsigned int trust; switch( *p ) { case '1': trust = TRUST_UNDEFINED; break; case '2': trust = TRUST_NEVER ; break; case '3': trust = TRUST_MARGINAL ; break; case '4': trust = TRUST_FULLY ; break; case '5': trust = TRUST_ULTIMATE ; break; default: BUG(); } if (trust == TRUST_ULTIMATE && !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay", _("Do you really want to set this key" " to ultimate trust? (y/N) "))) ; /* no */ else { *new_trust = trust; changed = 1; break; } } #if 0 /* not yet implemented */ else if( *p == ans[0] || *p == ans[1] ) { tty_printf(_("Certificates leading to an ultimately trusted key:\n")); show = 1; break; } #endif else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) { break ; /* back to the menu */ } else if( !mode && (*p == ans[6] || *p == ans[7] ) ) { break; /* skip */ } else if( !mode && (*p == ans[4] || *p == ans[5] ) ) { quit = 1; break ; /* back to the menu */ } xfree(p); p = NULL; } xfree(p); return show? -2: quit? -1 : changed; } #endif /*!NO_TRUST_MODELS*/ /* * Display a menu to change the ownertrust of the key PK (which should * be a primary key). * For mode values see do_edit_ownertrust () */ #ifndef NO_TRUST_MODELS int edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode ) { unsigned int trust = 0; int no_help = 0; for(;;) { switch ( do_edit_ownertrust (ctrl, pk, mode, &trust, no_help ) ) { case -1: /* quit */ return -1; case -2: /* show info */ no_help = 1; break; case 1: /* trust value set */ trust &= ~TRUST_FLAG_DISABLED; trust |= get_ownertrust (pk) & TRUST_FLAG_DISABLED; update_ownertrust (pk, trust ); return 1; default: return 0; } } } #endif /*!NO_TRUST_MODELS*/ /**************** * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL * Returns: true if we trust. */ static int do_we_trust( PKT_public_key *pk, unsigned int trustlevel ) { /* We should not be able to get here with a revoked or expired key */ if(trustlevel & TRUST_FLAG_REVOKED || trustlevel & TRUST_FLAG_SUB_REVOKED || (trustlevel & TRUST_MASK) == TRUST_EXPIRED) BUG(); if( opt.trust_model==TM_ALWAYS ) { if( opt.verbose ) log_info("No trust check due to '--trust-model always' option\n"); return 1; } switch(trustlevel & TRUST_MASK) { default: log_error ("invalid trustlevel %u returned from validation layer\n", trustlevel); /* fall through */ case TRUST_UNKNOWN: case TRUST_UNDEFINED: log_info(_("%s: There is no assurance this key belongs" " to the named user\n"),keystr_from_pk(pk)); return 0; /* no */ case TRUST_MARGINAL: log_info(_("%s: There is limited assurance this key belongs" " to the named user\n"),keystr_from_pk(pk)); return 1; /* yes */ case TRUST_FULLY: if( opt.verbose ) log_info(_("This key probably belongs to the named user\n")); return 1; /* yes */ case TRUST_ULTIMATE: if( opt.verbose ) log_info(_("This key belongs to us\n")); return 1; /* yes */ case TRUST_NEVER: /* This can be returned by TOFU, which can return negative assertions. */ log_info(_("%s: This key is bad! It has been marked as untrusted!\n"), keystr_from_pk(pk)); return 0; /* no */ } return 1; /*NOTREACHED*/ } /**************** * wrapper around do_we_trust, so we can ask whether to use the * key anyway. */ static int do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel ) { int rc; rc = do_we_trust( pk, trustlevel ); if( !opt.batch && !rc ) { print_pubkey_info(NULL,pk); print_fingerprint (NULL, pk, 2); tty_printf("\n"); if ((trustlevel & TRUST_MASK) == TRUST_NEVER) tty_printf( _("This key has is bad! It has been marked as untrusted! If you\n" "*really* know what you are doing, you may answer the next\n" "question with yes.\n")); else tty_printf( _("It is NOT certain that the key belongs to the person named\n" "in the user ID. If you *really* know what you are doing,\n" "you may answer the next question with yes.\n")); tty_printf("\n"); if (is_status_enabled ()) { u32 kid[2]; char *hint_str; keyid_from_pk (pk, kid); hint_str = get_long_user_id_string ( kid ); write_status_text ( STATUS_USERID_HINT, hint_str ); xfree (hint_str); } if( cpr_get_answer_is_yes("untrusted_key.override", _("Use this key anyway? (y/N) ")) ) rc = 1; /* Hmmm: Should we set a flag to tell the user about * his decision the next time he encrypts for this recipient? */ } return rc; } /* Write a TRUST_foo status line inclduing the validation model. */ static void write_trust_status (int statuscode, int trustlevel) { #ifdef NO_TRUST_MODELS write_status (statuscode); #else /* NO_TRUST_MODELS */ int tm; /* For the combined tofu+pgp method, we return the trust model which * was responsible for the trustlevel. */ if (opt.trust_model == TM_TOFU_PGP) tm = (trustlevel & TRUST_FLAG_TOFU_BASED)? TM_TOFU : TM_PGP; else tm = opt.trust_model; write_status_strings (statuscode, "0 ", trust_model_string (tm), NULL); #endif /* NO_TRUST_MODELS */ } /**************** * Check whether we can trust this signature. * Returns an error code if we should not trust this signature. */ int check_signatures_trust (ctrl_t ctrl, PKT_signature *sig) { PKT_public_key *pk = xmalloc_clear( sizeof *pk ); unsigned int trustlevel = TRUST_UNKNOWN; int rc=0; rc = get_pubkey( pk, sig->keyid ); if (rc) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); rc = GPG_ERR_NO_PUBKEY; goto leave; } if ( opt.trust_model==TM_ALWAYS ) { if( !opt.quiet ) log_info(_("WARNING: Using untrusted key!\n")); if (opt.with_fingerprint) print_fingerprint (NULL, pk, 1); goto leave; } if(pk->flags.maybe_revoked && !pk->flags.revoked) log_info(_("WARNING: this key might be revoked (revocation key" " not present)\n")); - trustlevel = get_validity (ctrl, pk, NULL, sig, 1); + trustlevel = get_validity (ctrl, NULL, pk, NULL, sig, 1); if ( (trustlevel & TRUST_FLAG_REVOKED) ) { write_status( STATUS_KEYREVOKED ); if(pk->flags.revoked == 2) log_info(_("WARNING: This key has been revoked by its" " designated revoker!\n")); else log_info(_("WARNING: This key has been revoked by its owner!\n")); log_info(_(" This could mean that the signature is forged.\n")); show_revocation_reason( pk, 0 ); } else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This subkey has been revoked by its owner!\n")); show_revocation_reason( pk, 0 ); } if ((trustlevel & TRUST_FLAG_DISABLED)) log_info (_("Note: This key has been disabled.\n")); /* If we have PKA information adjust the trustlevel. */ if (sig->pka_info && sig->pka_info->valid) { unsigned char fpr[MAX_FINGERPRINT_LEN]; PKT_public_key *primary_pk; size_t fprlen; int okay; primary_pk = xmalloc_clear (sizeof *primary_pk); get_pubkey (primary_pk, pk->main_keyid); fingerprint_from_pk (primary_pk, fpr, &fprlen); free_public_key (primary_pk); if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) ) { okay = 1; write_status_text (STATUS_PKA_TRUST_GOOD, sig->pka_info->email); log_info (_("Note: Verified signer's address is '%s'\n"), sig->pka_info->email); } else { okay = 0; write_status_text (STATUS_PKA_TRUST_BAD, sig->pka_info->email); log_info (_("Note: Signer's address '%s' " "does not match DNS entry\n"), sig->pka_info->email); } switch ( (trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: case TRUST_UNDEFINED: case TRUST_MARGINAL: if (okay && opt.verify_options&VERIFY_PKA_TRUST_INCREASE) { trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY); log_info (_("trustlevel adjusted to FULL" " due to valid PKA info\n")); } /* (fall through) */ case TRUST_FULLY: if (!okay) { trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER); log_info (_("trustlevel adjusted to NEVER" " due to bad PKA info\n")); } break; } } /* Now let the user know what up with the trustlevel. */ switch ( (trustlevel & TRUST_MASK) ) { case TRUST_EXPIRED: log_info(_("Note: This key has expired!\n")); print_fingerprint (NULL, pk, 1); break; default: log_error ("invalid trustlevel %u returned from validation layer\n", trustlevel); /* fall through */ case TRUST_UNKNOWN: case TRUST_UNDEFINED: write_trust_status (STATUS_TRUST_UNDEFINED, trustlevel); log_info(_("WARNING: This key is not certified with" " a trusted signature!\n")); log_info(_(" There is no indication that the " "signature belongs to the owner.\n" )); print_fingerprint (NULL, pk, 1); break; case TRUST_NEVER: /* This level can be returned by TOFU, which supports negative * assertions. */ write_trust_status (STATUS_TRUST_NEVER, trustlevel); log_info(_("WARNING: We do NOT trust this key!\n")); log_info(_(" The signature is probably a FORGERY.\n")); if (opt.with_fingerprint) print_fingerprint (NULL, pk, 1); rc = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case TRUST_MARGINAL: write_trust_status (STATUS_TRUST_MARGINAL, trustlevel); log_info(_("WARNING: This key is not certified with" " sufficiently trusted signatures!\n")); log_info(_(" It is not certain that the" " signature belongs to the owner.\n" )); print_fingerprint (NULL, pk, 1); break; case TRUST_FULLY: write_trust_status (STATUS_TRUST_FULLY, trustlevel); if (opt.with_fingerprint) print_fingerprint (NULL, pk, 1); break; case TRUST_ULTIMATE: write_trust_status (STATUS_TRUST_ULTIMATE, trustlevel); if (opt.with_fingerprint) print_fingerprint (NULL, pk, 1); break; } leave: free_public_key( pk ); return rc; } void release_pk_list (pk_list_t pk_list) { PK_LIST pk_rover; for ( ; pk_list; pk_list = pk_rover) { pk_rover = pk_list->next; free_public_key ( pk_list->pk ); xfree ( pk_list ); } } static int key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk) { for( ; pk_list; pk_list = pk_list->next) if (cmp_public_keys(pk_list->pk, pk) == 0) return 0; return -1; } /**************** * Return a malloced string with a default recipient if there is any */ static char * default_recipient(ctrl_t ctrl) { PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN+1]; size_t n; char *p; int i; if( opt.def_recipient ) return xstrdup( opt.def_recipient ); if( !opt.def_recipient_self ) return NULL; pk = xmalloc_clear( sizeof *pk ); i = get_seckey_default (ctrl, pk); if( i ) { free_public_key( pk ); return NULL; } n = MAX_FINGERPRINT_LEN; fingerprint_from_pk( pk, fpr, &n ); free_public_key( pk ); p = xmalloc( 2*n+3 ); *p++ = '0'; *p++ = 'x'; for(i=0; i < n; i++ ) sprintf( p+2*i, "%02X", fpr[i] ); p -= 2; return p; } static int expand_id(const char *id,strlist_t *into,unsigned int flags) { struct groupitem *groups; int count=0; for(groups=opt.grouplist;groups;groups=groups->next) { /* need strcasecmp() here, as this should be localized */ if(strcasecmp(groups->name,id)==0) { strlist_t each,sl; /* this maintains the current utf8-ness */ for(each=groups->values;each;each=each->next) { sl=add_to_strlist(into,each->d); sl->flags=flags; count++; } break; } } return count; } /* For simplicity, and to avoid potential loops, we only expand once - * you can't make an alias that points to an alias. */ static strlist_t expand_group (strlist_t input) { strlist_t output = NULL; strlist_t sl, rover; for (rover = input; rover; rover = rover->next) if (!(rover->flags & PK_LIST_FROM_FILE) && !expand_id(rover->d,&output,rover->flags)) { /* Didn't find any groups, so use the existing string */ sl=add_to_strlist(&output,rover->d); sl->flags=rover->flags; } return output; } /* Helper for build_pk_list to find and check one key. This helper is * also used directly in server mode by the RECIPIENTS command. On * success the new key is added to PK_LIST_ADDR. NAME is the user id * of the key. USE the requested usage and a set MARK_HIDDEN will * mark the key in the updated list as a hidden recipient. If * FROM_FILE is true, NAME is is not a user ID but the name of a file * holding a key. */ gpg_error_t find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, int mark_hidden, int from_file, pk_list_t *pk_list_addr) { int rc; PKT_public_key *pk; if (!name || !*name) return gpg_error (GPG_ERR_INV_USER_ID); pk = xtrycalloc (1, sizeof *pk); if (!pk) return gpg_error_from_syserror (); pk->req_usage = use; if (from_file) rc = get_pubkey_fromfile (ctrl, pk, name); else rc = get_best_pubkey_byname (ctrl, NULL, pk, name, NULL, 0, 0); if (rc) { int code; /* Key not found or other error. */ log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) ); switch (gpg_err_code (rc)) { case GPG_ERR_NO_SECKEY: case GPG_ERR_NO_PUBKEY: code = 1; break; case GPG_ERR_INV_USER_ID: code = 14; break; default: code = 0; break; } send_status_inv_recp (code, name); free_public_key (pk); return rc; } rc = openpgp_pk_test_algo2 (pk->pubkey_algo, use); if (rc) { /* Key found but not usable for us (e.g. sign-only key). */ send_status_inv_recp (3, name); /* Wrong key usage */ log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) ); free_public_key (pk); return rc; } /* Key found and usable. Check validity. */ if (!from_file) { int trustlevel; - trustlevel = get_validity (ctrl, pk, pk->user_id, NULL, 1); + trustlevel = get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1); if ( (trustlevel & TRUST_FLAG_DISABLED) ) { /* Key has been disabled. */ send_status_inv_recp (13, name); log_info (_("%s: skipped: public key is disabled\n"), name); free_public_key (pk); return GPG_ERR_UNUSABLE_PUBKEY; } if ( !do_we_trust_pre (pk, trustlevel) ) { /* We don't trust this key. */ send_status_inv_recp (10, name); free_public_key (pk); return GPG_ERR_UNUSABLE_PUBKEY; } } /* Skip the actual key if the key is already present in the list. */ if (!key_present_in_pk_list (*pk_list_addr, pk)) { if (!opt.quiet) log_info (_("%s: skipped: public key already present\n"), name); free_public_key (pk); } else { pk_list_t r; r = xtrymalloc (sizeof *r); if (!r) { rc = gpg_error_from_syserror (); free_public_key (pk); return rc; } r->pk = pk; r->next = *pk_list_addr; r->flags = mark_hidden? 1:0; *pk_list_addr = r; } return 0; } /* This is the central function to collect the keys for recipients. * It is thus used to prepare a public key encryption. encrypt-to * keys, default keys and the keys for the actual recipients are all * collected here. When not in batch mode and no recipient has been * passed on the commandline, the function will also ask for * recipients. * * RCPTS is a string list with the recipients; NULL is an allowed * value but not very useful. Group expansion is done on these names; * they may be in any of the user Id formats we can handle. The flags * bits for each string in the string list are used for: * * - PK_LIST_ENCRYPT_TO :: This is an encrypt-to recipient. * - PK_LIST_HIDDEN :: This is a hidden recipient. * - PK_LIST_FROM_FILE :: The argument is a file with a key. * * On success a list of keys is stored at the address RET_PK_LIST; the * caller must free this list. On error the value at this address is * not changed. */ int build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list) { PK_LIST pk_list = NULL; PKT_public_key *pk=NULL; int rc=0; int any_recipients=0; strlist_t rov,remusr; char *def_rec = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; /* Try to expand groups if any have been defined. */ if (opt.grouplist) remusr = expand_group (rcpts); else remusr = rcpts; /* XXX: Change this function to use get_pubkeys instead of get_pubkey_byname to detect ambiguous key specifications and warn about duplicate keyblocks. For ambiguous key specifications on the command line or provided interactively, prompt the user to select the best key. If a key specification is ambiguous and we are in batch mode, die. */ if (opt.encrypt_to_default_key) { static int warned; const char *default_key = parse_def_secret_key (ctrl); if (default_key) { PK_LIST r = xmalloc_clear (sizeof *r); r->pk = xmalloc_clear (sizeof *r->pk); r->pk->req_usage = PUBKEY_USAGE_ENC; rc = get_pubkey_byname (ctrl, NULL, r->pk, default_key, NULL, NULL, 0, 1); if (rc) { xfree (r->pk); xfree (r); log_error (_("can't encrypt to '%s'\n"), default_key); if (!opt.quiet) log_info (_("(check argument of option '%s')\n"), "--default-key"); } else { r->next = pk_list; r->flags = 0; pk_list = r; } } else if (opt.def_secret_key) { if (! warned) log_info (_("option '%s' given, but no valid default keys given\n"), "--encrypt-to-default-key"); warned = 1; } else { if (! warned) log_info (_("option '%s' given, but option '%s' not given\n"), "--encrypt-to-default-key", "--default-key"); warned = 1; } } /* Check whether there are any recipients in the list and build the * list of the encrypt-to ones (we always trust them). */ for ( rov = remusr; rov; rov = rov->next ) { if ( !(rov->flags & PK_LIST_ENCRYPT_TO) ) { /* This is a regular recipient; i.e. not an encrypt-to one. */ any_recipients = 1; /* Hidden recipients are not allowed while in PGP mode, issue a warning and switch into GnuPG mode. */ if ((rov->flags & PK_LIST_HIDDEN) && (PGP6 || PGP7 || PGP8)) { log_info(_("you may not use %s while in %s mode\n"), "--hidden-recipient", compliance_option_string()); compliance_failure(); } } else if (!opt.no_encrypt_to) { /* --encrypt-to has not been disabled. Check this encrypt-to key. */ pk = xmalloc_clear( sizeof *pk ); pk->req_usage = PUBKEY_USAGE_ENC; /* We explicitly allow encrypt-to to an disabled key; thus we pass 1 for the second last argument and 1 as the last argument to disable AKL. */ if ( (rc = get_pubkey_byname (ctrl, NULL, pk, rov->d, NULL, NULL, 1, 1)) ) { free_public_key ( pk ); pk = NULL; log_error (_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) ); send_status_inv_recp (0, rov->d); goto fail; } else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo, PUBKEY_USAGE_ENC)) ) { /* Skip the actual key if the key is already present * in the list. Add it to our list if not. */ if (key_present_in_pk_list(pk_list, pk) == 0) { free_public_key (pk); pk = NULL; if (!opt.quiet) log_info (_("%s: skipped: public key already present\n"), rov->d); } else { PK_LIST r; r = xmalloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->flags = (rov->flags&PK_LIST_HIDDEN)?1:0; pk_list = r; /* Hidden encrypt-to recipients are not allowed while in PGP mode, issue a warning and switch into GnuPG mode. */ if ((r->flags&PK_LIST_ENCRYPT_TO) && (PGP6 || PGP7 || PGP8)) { log_info(_("you may not use %s while in %s mode\n"), "--hidden-encrypt-to", compliance_option_string()); compliance_failure(); } } } else { /* The public key is not usable for encryption. */ free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) ); send_status_inv_recp (3, rov->d); /* Wrong key usage */ goto fail; } } } /* If we don't have any recipients yet and we are not in batch mode drop into interactive selection mode. */ if ( !any_recipients && !opt.batch ) { int have_def_rec; char *answer = NULL; strlist_t backlog = NULL; if (pk_list) any_recipients = 1; def_rec = default_recipient(ctrl); have_def_rec = !!def_rec; if ( !have_def_rec ) tty_printf(_("You did not specify a user ID. (you may use \"-r\")\n")); for (;;) { rc = 0; xfree(answer); if ( have_def_rec ) { /* A default recipient is taken as the first entry. */ answer = def_rec; def_rec = NULL; } else if (backlog) { /* This is part of our trick to expand and display groups. */ answer = strlist_pop (&backlog); } else { /* Show the list of already collected recipients and ask for more. */ PK_LIST iter; tty_printf("\n"); tty_printf(_("Current recipients:\n")); for (iter=pk_list;iter;iter=iter->next) { u32 keyid[2]; keyid_from_pk(iter->pk,keyid); tty_printf ("%s/%s %s \"", pubkey_string (iter->pk, pkstrbuf, sizeof pkstrbuf), keystr(keyid), datestr_from_pk (iter->pk)); if (iter->pk->user_id) tty_print_utf8_string(iter->pk->user_id->name, iter->pk->user_id->len); else { size_t n; char *p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ); xfree(p); } tty_printf("\"\n"); } answer = cpr_get_utf8("pklist.user_id.enter", _("\nEnter the user ID. " "End with an empty line: ")); trim_spaces(answer); cpr_kill_prompt(); } if ( !answer || !*answer ) { xfree(answer); break; /* No more recipients entered - get out of loop. */ } /* Do group expand here too. The trick here is to continue the loop if any expansion occurred. The code above will then list all expanded keys. */ if (expand_id(answer,&backlog,0)) continue; /* Get and check key for the current name. */ free_public_key (pk); pk = xmalloc_clear( sizeof *pk ); pk->req_usage = PUBKEY_USAGE_ENC; rc = get_pubkey_byname (ctrl, NULL, pk, answer, NULL, NULL, 0, 0 ); if (rc) tty_printf(_("No such user ID.\n")); else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo, PUBKEY_USAGE_ENC)) ) { if ( have_def_rec ) { /* No validation for a default recipient. */ if (!key_present_in_pk_list(pk_list, pk)) { free_public_key (pk); pk = NULL; log_info (_("skipped: public key " "already set as default recipient\n") ); } else { PK_LIST r = xmalloc (sizeof *r); r->pk = pk; pk = NULL; r->next = pk_list; r->flags = 0; /* No throwing default ids. */ pk_list = r; } any_recipients = 1; continue; } else { /* Check validity of this key. */ int trustlevel; - trustlevel = get_validity (ctrl, pk, pk->user_id, NULL, 1); + trustlevel = + get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1); if ( (trustlevel & TRUST_FLAG_DISABLED) ) { tty_printf (_("Public key is disabled.\n") ); } else if ( do_we_trust_pre (pk, trustlevel) ) { /* Skip the actual key if the key is already * present in the list */ if (!key_present_in_pk_list(pk_list, pk)) { free_public_key (pk); pk = NULL; log_info(_("skipped: public key already set\n") ); } else { PK_LIST r; r = xmalloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->flags = 0; /* No throwing interactive ids. */ pk_list = r; } any_recipients = 1; continue; } } } xfree(def_rec); def_rec = NULL; have_def_rec = 0; } if ( pk ) { free_public_key( pk ); pk = NULL; } } else if ( !any_recipients && (def_rec = default_recipient(ctrl)) ) { /* We are in batch mode and have only a default recipient. */ pk = xmalloc_clear( sizeof *pk ); pk->req_usage = PUBKEY_USAGE_ENC; /* The default recipient is allowed to be disabled; thus pass 1 as second last argument. We also don't want an AKL. */ rc = get_pubkey_byname (ctrl, NULL, pk, def_rec, NULL, NULL, 1, 1); if (rc) log_error(_("unknown default recipient \"%s\"\n"), def_rec ); else if ( !(rc=openpgp_pk_test_algo2(pk->pubkey_algo, PUBKEY_USAGE_ENC)) ) { /* Mark any_recipients here since the default recipient would have been used if it wasn't already there. It doesn't really matter if we got this key from the default recipient or an encrypt-to. */ any_recipients = 1; if (!key_present_in_pk_list(pk_list, pk)) log_info (_("skipped: public key already set " "as default recipient\n")); else { PK_LIST r = xmalloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->flags = 0; /* No throwing default ids. */ pk_list = r; } } if ( pk ) { free_public_key( pk ); pk = NULL; } xfree(def_rec); def_rec = NULL; } else { /* General case: Check all keys. */ any_recipients = 0; for (; remusr; remusr = remusr->next ) { if ( (remusr->flags & PK_LIST_ENCRYPT_TO) ) continue; /* encrypt-to keys are already handled. */ rc = find_and_check_key (ctrl, remusr->d, PUBKEY_USAGE_ENC, !!(remusr->flags&PK_LIST_HIDDEN), !!(remusr->flags&PK_LIST_FROM_FILE), &pk_list); if (rc) goto fail; any_recipients = 1; } } if ( !rc && !any_recipients ) { log_error(_("no valid addressees\n")); write_status_text (STATUS_NO_RECP, "0"); rc = GPG_ERR_NO_USER_ID; } #ifdef USE_TOFU if (! rc && (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU)) { PK_LIST iter; for (iter = pk_list; iter; iter = iter->next) { int rc2; /* Note: we already resolved any conflict when looking up the key. Don't annoy the user again if she selected accept once. */ rc2 = tofu_register_encryption (ctrl, iter->pk, NULL, 0); if (rc2) log_info ("WARNING: Failed to register encryption to %s" " with TOFU engine\n", keystr (pk_main_keyid (iter->pk))); else if (DBG_TRUST) log_debug ("Registered encryption to %s with TOFU DB.\n", keystr (pk_main_keyid (iter->pk))); } } #endif /*USE_TOFU*/ fail: if ( rc ) release_pk_list( pk_list ); else *ret_pk_list = pk_list; if (opt.grouplist) free_strlist(remusr); return rc; } /* In pgp6 mode, disallow all ciphers except IDEA (1), 3DES (2), and CAST5 (3), all hashes except MD5 (1), SHA1 (2), and RIPEMD160 (3), and all compressions except none (0) and ZIP (1). pgp7 and pgp8 mode expands the cipher list to include AES128 (7), AES192 (8), AES256 (9), and TWOFISH (10). pgp8 adds the SHA-256 hash (8). For a true PGP key all of this is unneeded as they are the only items present in the preferences subpacket, but checking here covers the weird case of encrypting to a key that had preferences from a different implementation which was then used with PGP. I am not completely comfortable with this as the right thing to do, as it slightly alters the list of what the user is supposedly requesting. It is not against the RFC however, as the preference chosen will never be one that the user didn't specify somewhere ("The implementation may use any mechanism to pick an algorithm in the intersection"), and PGP has no mechanism to fix such a broken preference list, so I'm including it. -dms */ int algo_available( preftype_t preftype, int algo, const union pref_hint *hint) { if( preftype == PREFTYPE_SYM ) { if(PGP6 && (algo != CIPHER_ALGO_IDEA && algo != CIPHER_ALGO_3DES && algo != CIPHER_ALGO_CAST5)) return 0; if(PGP7 && (algo != CIPHER_ALGO_IDEA && algo != CIPHER_ALGO_3DES && algo != CIPHER_ALGO_CAST5 && algo != CIPHER_ALGO_AES && algo != CIPHER_ALGO_AES192 && algo != CIPHER_ALGO_AES256 && algo != CIPHER_ALGO_TWOFISH)) return 0; /* PGP8 supports all the ciphers we do.. */ return algo && !openpgp_cipher_test_algo ( algo ); } else if( preftype == PREFTYPE_HASH ) { if (hint && hint->digest_length) { if (hint->digest_length!=20 || opt.flags.dsa2) { /* If --enable-dsa2 is set or the hash isn't 160 bits (which implies DSA2), then we'll accept a hash that is larger than we need. Otherwise we won't accept any hash that isn't exactly the right size. */ if (hint->digest_length > gcry_md_get_algo_dlen (algo)) return 0; } else if (hint->digest_length != gcry_md_get_algo_dlen (algo)) return 0; } if((PGP6 || PGP7) && (algo != DIGEST_ALGO_MD5 && algo != DIGEST_ALGO_SHA1 && algo != DIGEST_ALGO_RMD160)) return 0; if(PGP8 && (algo != DIGEST_ALGO_MD5 && algo != DIGEST_ALGO_SHA1 && algo != DIGEST_ALGO_RMD160 && algo != DIGEST_ALGO_SHA256)) return 0; return algo && !openpgp_md_test_algo (algo); } else if( preftype == PREFTYPE_ZIP ) { if((PGP6 || PGP7) && (algo != COMPRESS_ALGO_NONE && algo != COMPRESS_ALGO_ZIP)) return 0; /* PGP8 supports all the compression algos we do */ return !check_compress_algo( algo ); } else return 0; } /**************** * Return -1 if we could not find an algorithm. */ int select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, const union pref_hint *hint) { PK_LIST pkr; u32 bits[8]; const prefitem_t *prefs; int result=-1,i; u16 scores[256]; if( !pk_list ) return -1; memset(bits,0xFF,sizeof(bits)); memset(scores,0,sizeof(scores)); for( pkr = pk_list; pkr; pkr = pkr->next ) { u32 mask[8]; int rank=1,implicit=-1; memset(mask,0,sizeof(mask)); switch(preftype) { case PREFTYPE_SYM: /* IDEA is implicitly there for v3 keys with v3 selfsigs if --pgp2 mode is on. This was a 2440 thing that was dropped from 4880 but is still relevant to GPG's 1991 support. All this doesn't mean IDEA is actually available, of course. */ implicit=CIPHER_ALGO_3DES; break; case PREFTYPE_HASH: /* While I am including this code for completeness, note that currently --pgp2 mode locks the hash at MD5, so this code will never even be called. Even if the hash wasn't locked at MD5, we don't support sign+encrypt in --pgp2 mode, and that's the only time PREFTYPE_HASH is used anyway. -dms */ implicit=DIGEST_ALGO_SHA1; break; case PREFTYPE_ZIP: /* Uncompressed is always an option. */ implicit=COMPRESS_ALGO_NONE; } if (pkr->pk->user_id) /* selected by user ID */ prefs = pkr->pk->user_id->prefs; else prefs = pkr->pk->prefs; if( prefs ) { for (i=0; prefs[i].type; i++ ) { if( prefs[i].type == preftype ) { /* Make sure all scores don't add up past 0xFFFF (and roll around) */ if(rank+scores[prefs[i].value]<=0xFFFF) scores[prefs[i].value]+=rank; else scores[prefs[i].value]=0xFFFF; mask[prefs[i].value/32] |= 1<<(prefs[i].value%32); rank++; /* We saw the implicit algorithm, so we don't need tack it on the end ourselves. */ if(implicit==prefs[i].value) implicit=-1; } } } if(rank==1 && preftype==PREFTYPE_ZIP) { /* If the compression preferences are not present, they are assumed to be ZIP, Uncompressed (RFC4880:13.3.1) */ scores[1]=1; /* ZIP is first choice */ scores[0]=2; /* Uncompressed is second choice */ mask[0]|=3; } /* If the key didn't have the implicit algorithm listed explicitly, add it here at the tail of the list. */ if(implicit>-1) { scores[implicit]+=rank; mask[implicit/32] |= 1<<(implicit%32); } for(i=0;i<8;i++) bits[i]&=mask[i]; } /* We've now scored all of the algorithms, and the usable ones have bits set. Let's pick the winner. */ /* The caller passed us a request. Can we use it? */ if(request>-1 && (bits[request/32] & (1<<(request%32))) && algo_available(preftype,request,hint)) result=request; if(result==-1) { /* If we have personal prefs set, use them. */ prefs=NULL; if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs) prefs=opt.personal_cipher_prefs; else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs) prefs=opt.personal_digest_prefs; else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs) prefs=opt.personal_compress_prefs; if( prefs ) for(i=0; prefs[i].type; i++ ) { if(bits[prefs[i].value/32] & (1<<(prefs[i].value%32)) && algo_available( preftype, prefs[i].value, hint)) { result = prefs[i].value; break; } } } if(result==-1) { unsigned int best=-1; /* At this point, we have not selected an algorithm due to a special request or via personal prefs. Pick the highest ranked algorithm (i.e. the one with the lowest score). */ if(preftype==PREFTYPE_HASH && scores[DIGEST_ALGO_MD5]) { /* "If you are building an authentication system, the recipient may specify a preferred signing algorithm. However, the signer would be foolish to use a weak algorithm simply because the recipient requests it." (RFC4880:14). If any other hash algorithm is available, pretend that MD5 isn't. Note that if the user intentionally chose MD5 by putting it in their personal prefs, then we do what the user said (as we never reach this code). */ for(i=DIGEST_ALGO_MD5+1;i<256;i++) if(scores[i]) { scores[DIGEST_ALGO_MD5]=0; break; } } for(i=0;i<256;i++) { /* Note the '<' here. This means in case of a tie, we will favor the lower algorithm number. We have a choice between the lower number (probably an older algorithm with more time in use), or the higher number (probably a newer algorithm with less time in use). Older is probably safer here, even though the newer algorithms tend to be "stronger". */ if(scores[i] && scores[i]next) { int mdc; if (pkr->pk->user_id) /* selected by user ID */ mdc = pkr->pk->user_id->flags.mdc; else mdc = pkr->pk->flags.mdc; if (!mdc) return 0; /* At least one recipient does not support it. */ } return 1; /* Can be used. */ } /* Print a warning for all keys in PK_LIST missing the MDC feature. */ void warn_missing_mdc_from_pklist (PK_LIST pk_list) { PK_LIST pkr; for (pkr = pk_list; pkr; pkr = pkr->next) { int mdc; if (pkr->pk->user_id) /* selected by user ID */ mdc = pkr->pk->user_id->flags.mdc; else mdc = pkr->pk->flags.mdc; if (!mdc) log_info (_("Note: key %s has no %s feature\n"), keystr_from_pk (pkr->pk), "MDC"); } } void warn_missing_aes_from_pklist (PK_LIST pk_list) { PK_LIST pkr; for (pkr = pk_list; pkr; pkr = pkr->next) { const prefitem_t *prefs; int i; int gotit = 0; prefs = pkr->pk->user_id? pkr->pk->user_id->prefs : pkr->pk->prefs; if (prefs) { for (i=0; !gotit && prefs[i].type; i++ ) if (prefs[i].type == PREFTYPE_SYM && prefs[i].value == CIPHER_ALGO_AES) gotit++; } if (!gotit) log_info (_("Note: key %s has no preference for %s\n"), keystr_from_pk (pkr->pk), "AES"); } } diff --git a/g10/test-stubs.c b/g10/test-stubs.c index 2dc65ab4d..8752f8894 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -1,528 +1,531 @@ /* test-stubs.c - The GnuPG signature verify utility * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2006, * 2008, 2009, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" #include "util.h" #include "packet.h" #include "iobuf.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" #include "sysutils.h" #include "status.h" #include "call-agent.h" int g10_errors_seen; void g10_exit( int rc ) { rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; exit(rc ); } /* Stub: * We have to override the trustcheck from pkclist.c because * this utility assumes that all keys in the keyring are trustworthy */ int check_signatures_trust (ctrl_t ctrl, PKT_signature *sig) { (void)ctrl; (void)sig; return 0; } void read_trust_options(byte *trust_model, ulong *created, ulong *nextcheck, byte *marginals, byte *completes, byte *cert_depth, byte *min_cert_level) { (void)trust_model; (void)created; (void)nextcheck; (void)marginals; (void)completes; (void)cert_depth; (void)min_cert_level; } /* Stub: * We don't have the trustdb , so we have to provide some stub functions * instead */ int cache_disabled_value(PKT_public_key *pk) { (void)pk; return 0; } void check_trustdb_stale (ctrl_t ctrl) { (void)ctrl; } int -get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) +get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, + PKT_user_id *uid) { (void)ctrl; + (void)kb; (void)pk; (void)uid; return '?'; } unsigned int -get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid, +get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig, int may_ask) { (void)ctrl; + (void)kb; (void)pk; (void)uid; (void)sig; (void)may_ask; return 0; } const char * trust_value_to_string (unsigned int value) { (void)value; return "err"; } const char * uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid) { (void)ctrl; (void)key; (void)uid; return "err"; } int get_ownertrust_info (PKT_public_key *pk) { (void)pk; return '?'; } unsigned int get_ownertrust (PKT_public_key *pk) { (void)pk; return TRUST_UNKNOWN; } /* Stubs: * Because we only work with trusted keys, it does not make sense to * get them from a keyserver */ struct keyserver_spec * keyserver_match (struct keyserver_spec *spec) { (void)spec; return NULL; } int keyserver_any_configured (ctrl_t ctrl) { (void)ctrl; return 0; } int keyserver_import_keyid (u32 *keyid, void *dummy, int quick) { (void)keyid; (void)dummy; (void)quick; return -1; } int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, struct keyserver_spec *keyserver, int quick) { (void)ctrl; (void)fprint; (void)fprint_len; (void)keyserver; (void)quick; return -1; } int keyserver_import_cert (const char *name) { (void)name; return -1; } int keyserver_import_pka (const char *name,unsigned char *fpr) { (void)name; (void)fpr; return -1; } gpg_error_t keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, unsigned char **fpr, size_t *fpr_len) { (void)ctrl; (void)name; (void)quick; (void)fpr; (void)fpr_len; return GPG_ERR_BUG; } int keyserver_import_name (const char *name,struct keyserver_spec *spec) { (void)name; (void)spec; return -1; } int keyserver_import_ldap (const char *name) { (void)name; return -1; } gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock) { (void)ctrl; (void)fname; (void)r_keyblock; return -1; } /* Stub: * No encryption here but mainproc links to these functions. */ gpg_error_t get_session_key (ctrl_t ctrl, PKT_pubkey_enc *k, DEK *dek) { (void)ctrl; (void)k; (void)dek; return GPG_ERR_GENERAL; } /* Stub: */ gpg_error_t get_override_session_key (DEK *dek, const char *string) { (void)dek; (void)string; return GPG_ERR_GENERAL; } /* Stub: */ int decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) { (void)ctrl; (void)procctx; (void)ed; (void)dek; return GPG_ERR_GENERAL; } /* Stub: * No interactive commands, so we don't need the helptexts */ void display_online_help (const char *keyword) { (void)keyword; } /* Stub: * We don't use secret keys, but getkey.c links to this */ int check_secret_key (PKT_public_key *pk, int n) { (void)pk; (void)n; return GPG_ERR_GENERAL; } /* Stub: * No secret key, so no passphrase needed */ DEK * passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache, const char *tmp, int *canceled) { (void)cipher_algo; (void)s2k; (void)create; (void)nocache; (void)tmp; if (canceled) *canceled = 0; return NULL; } void passphrase_clear_cache (const char *cacheid) { (void)cacheid; } struct keyserver_spec * parse_preferred_keyserver(PKT_signature *sig) { (void)sig; return NULL; } struct keyserver_spec * parse_keyserver_uri (const char *uri, int require_scheme, const char *configname, unsigned int configlineno) { (void)uri; (void)require_scheme; (void)configname; (void)configlineno; return NULL; } void free_keyserver_spec (struct keyserver_spec *keyserver) { (void)keyserver; } /* Stubs to avoid linking to photoid.c */ void show_photos (const struct user_attribute *attrs, int count, PKT_public_key *pk) { (void)attrs; (void)count; (void)pk; } int parse_image_header (const struct user_attribute *attr, byte *type, u32 *len) { (void)attr; (void)type; (void)len; return 0; } char * image_type_to_string (byte type, int string) { (void)type; (void)string; return NULL; } #ifdef ENABLE_CARD_SUPPORT int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { (void)name; (void)info; return 0; } #endif /* ENABLE_CARD_SUPPORT */ /* We do not do any locking, so use these stubs here */ void dotlock_disable (void) { } dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags) { (void)file_to_lock; (void)flags; return NULL; } void dotlock_destroy (dotlock_t h) { (void)h; } int dotlock_take (dotlock_t h, long timeout) { (void)h; (void)timeout; return 0; } int dotlock_release (dotlock_t h) { (void)h; return 0; } void dotlock_remove_lockfiles (void) { } gpg_error_t agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { (void)ctrl; (void)pk; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { (void)ctrl; (void)keyblock; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno, int *r_cleartext) { (void)ctrl; (void)hexkeygrip; (void)r_cleartext; *r_serialno = NULL; return gpg_error (GPG_ERR_NO_SECKEY); } gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, unsigned char **r_fpr, size_t *r_fprlen, char **r_url) { (void)ctrl; (void)userid; if (r_fpr) *r_fpr = NULL; if (r_fprlen) *r_fprlen = 0; if (r_url) *r_url = NULL; return gpg_error (GPG_ERR_NOT_FOUND); } gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen) { (void)ctrl; (void)keyspec; (void)options; (void)stats; *r_keyblock = NULL; *r_data = NULL; *r_datalen = 0; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } gpg_error_t tofu_write_tfs_record (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, const char *user_id) { (void)ctrl; (void)fp; (void)pk; (void)user_id; return gpg_error (GPG_ERR_GENERAL); } gpg_error_t tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id, enum tofu_policy *policy) { (void)ctrl; (void)pk; (void)user_id; (void)policy; return gpg_error (GPG_ERR_GENERAL); } const char * tofu_policy_str (enum tofu_policy policy) { (void)policy; return "unknown"; } void tofu_begin_batch_update (ctrl_t ctrl) { (void)ctrl; } void tofu_end_batch_update (ctrl_t ctrl) { (void)ctrl; } gpg_error_t tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) { (void) ctrl; (void) kb; return 0; } diff --git a/g10/trust.c b/g10/trust.c index 2a829b8b4..080926a36 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -1,746 +1,769 @@ /* trust.c - High level trust functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2012 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include "gpg.h" #include "keydb.h" #include "util.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "trustdb.h" #include "host2net.h" /* Return true if key is disabled. Note that this is usually used via the pk_is_disabled macro. */ int cache_disabled_value (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return 0; #else return tdb_cache_disabled_value (pk); #endif } void register_trusted_keyid (u32 *keyid) { #ifdef NO_TRUST_MODELS (void)keyid; #else tdb_register_trusted_keyid (keyid); #endif } void register_trusted_key (const char *string) { #ifdef NO_TRUST_MODELS (void)string; #else tdb_register_trusted_key (string); #endif } /* * This function returns a letter for a trust value. Trust flags * are ignored. */ static int trust_letter (unsigned int value) { switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return '-'; case TRUST_EXPIRED: return 'e'; case TRUST_UNDEFINED: return 'q'; case TRUST_NEVER: return 'n'; case TRUST_MARGINAL: return 'm'; case TRUST_FULLY: return 'f'; case TRUST_ULTIMATE: return 'u'; default: return '?'; } } /* The strings here are similar to those in pkclist.c:do_edit_ownertrust() */ const char * trust_value_to_string (unsigned int value) { switch ((value & TRUST_MASK)) { case TRUST_UNKNOWN: return _("unknown"); case TRUST_EXPIRED: return _("expired"); case TRUST_UNDEFINED: return _("undefined"); case TRUST_NEVER: return _("never"); case TRUST_MARGINAL: return _("marginal"); case TRUST_FULLY: return _("full"); case TRUST_ULTIMATE: return _("ultimate"); default: return "err"; } } int string_to_trust_value (const char *str) { if (!ascii_strcasecmp (str, "undefined")) return TRUST_UNDEFINED; else if (!ascii_strcasecmp (str, "never")) return TRUST_NEVER; else if (!ascii_strcasecmp (str, "marginal")) return TRUST_MARGINAL; else if (!ascii_strcasecmp (str, "full")) return TRUST_FULLY; else if (!ascii_strcasecmp(str, "ultimate")) return TRUST_ULTIMATE; else return -1; } const char * uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid) { if (!key && !uid) { /* TRANSLATORS: these strings are similar to those in trust_value_to_string(), but are a fixed length. This is needed to make attractive information listings where columns line up properly. The value "10" should be the length of the strings you choose to translate to. This is the length in printable columns. It gets passed to atoi() so everything after the number is essentially a comment and need not be translated. Either key and uid are both NULL, or neither are NULL. */ return _("10 translator see trust.c:uid_trust_string_fixed"); } else if(uid->is_revoked || (key && key->flags.revoked)) return _("[ revoked]"); else if(uid->is_expired) return _("[ expired]"); else if(key) { - switch (get_validity (ctrl, key, uid, NULL, 0) & TRUST_MASK) + switch (get_validity (ctrl, NULL, key, uid, NULL, 0) & TRUST_MASK) { case TRUST_UNKNOWN: return _("[ unknown]"); case TRUST_EXPIRED: return _("[ expired]"); case TRUST_UNDEFINED: return _("[ undef ]"); case TRUST_NEVER: return _("[ never ]"); case TRUST_MARGINAL: return _("[marginal]"); case TRUST_FULLY: return _("[ full ]"); case TRUST_ULTIMATE: return _("[ultimate]"); } } return "err"; } /* * Return the assigned ownertrust value for the given public key. * The key should be the primary key. */ unsigned int get_ownertrust (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return TRUST_UNKNOWN; #else return tdb_get_ownertrust (pk); #endif } /* * Same as get_ownertrust but this takes the minimum ownertrust value * into into account, and will bump up the value as needed. */ static int get_ownertrust_with_min (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return TRUST_UNKNOWN; #else unsigned int otrust, otrust_min; otrust = (tdb_get_ownertrust (pk) & TRUST_MASK); otrust_min = tdb_get_min_ownertrust (pk); if (otrust < otrust_min) { /* If the trust that the user has set is less than the trust that was calculated from a trust signature chain, use the higher of the two. We do this here and not in get_ownertrust since the underlying ownertrust should not really be set - just the appearance of the ownertrust. */ otrust = otrust_min; } return otrust; #endif } /* * Same as get_ownertrust but return a trust letter instead of an * value. This takes the minimum ownertrust value into account. */ int get_ownertrust_info (PKT_public_key *pk) { return trust_letter (get_ownertrust_with_min (pk)); } /* * Same as get_ownertrust but return a trust string instead of an * value. This takes the minimum ownertrust value into account. */ const char * get_ownertrust_string (PKT_public_key *pk) { return trust_value_to_string (get_ownertrust_with_min (pk)); } /* * Set the trust value of the given public key to the new value. * The key should be a primary one. */ void update_ownertrust (PKT_public_key *pk, unsigned int new_trust) { #ifdef NO_TRUST_MODELS (void)pk; (void)new_trust; #else tdb_update_ownertrust (pk, new_trust); #endif } int clear_ownertrusts (PKT_public_key *pk) { #ifdef NO_TRUST_MODELS (void)pk; return 0; #else return tdb_clear_ownertrusts (pk); #endif } void revalidation_mark (void) { #ifndef NO_TRUST_MODELS tdb_revalidation_mark (); #endif } void check_trustdb_stale (ctrl_t ctrl) { #ifndef NO_TRUST_MODELS tdb_check_trustdb_stale (ctrl); #else (void)ctrl; #endif } void check_or_update_trustdb (ctrl_t ctrl) { #ifndef NO_TRUST_MODELS tdb_check_or_update (ctrl); #else (void)ctrl; #endif } /* - * Return the validity information for PK. If the namehash is not - * NULL, the validity of the corresponding user ID is returned, - * otherwise, a reasonable value for the entire key is returned. + * Return the validity information for KB/PK (at least one must be + * non-NULL). If the namehash is not NULL, the validity of the + * corresponding user ID is returned, otherwise, a reasonable value + * for the entire key is returned. */ unsigned int -get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid, +get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig, int may_ask) { int rc; unsigned int validity; u32 kid[2]; PKT_public_key *main_pk; + if (kb && pk) + log_assert (keyid_cmp (pk_main_keyid (pk), + pk_main_keyid (kb->pkt->pkt.public_key)) == 0); + + if (! pk) + { + log_assert (kb); + pk = kb->pkt->pkt.public_key; + } + if (uid) namehash_from_uid (uid); keyid_from_pk (pk, kid); if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1]) { /* This is a subkey - get the mainkey. */ - main_pk = xmalloc_clear (sizeof *main_pk); - rc = get_pubkey (main_pk, pk->main_keyid); - if (rc) + if (kb) + main_pk = kb->pkt->pkt.public_key; + else { - char *tempkeystr = xstrdup (keystr (pk->main_keyid)); - log_error ("error getting main key %s of subkey %s: %s\n", - tempkeystr, keystr (kid), gpg_strerror (rc)); - xfree (tempkeystr); - validity = TRUST_UNKNOWN; - goto leave; - } + main_pk = xmalloc_clear (sizeof *main_pk); + rc = get_pubkey (main_pk, pk->main_keyid); + if (rc) + { + char *tempkeystr = xstrdup (keystr (pk->main_keyid)); + log_error ("error getting main key %s of subkey %s: %s\n", + tempkeystr, keystr (kid), gpg_strerror (rc)); + xfree (tempkeystr); + validity = TRUST_UNKNOWN; + goto leave; + } + } } else main_pk = pk; #ifdef NO_TRUST_MODELS validity = TRUST_UNKNOWN; #else - validity = tdb_get_validity_core (ctrl, pk, uid, main_pk, sig, may_ask); + validity = tdb_get_validity_core (ctrl, kb, pk, uid, main_pk, sig, may_ask); #endif leave: /* Set some flags direct from the key */ if (main_pk->flags.revoked) validity |= TRUST_FLAG_REVOKED; if (main_pk != pk && pk->flags.revoked) validity |= TRUST_FLAG_SUB_REVOKED; /* Note: expiration is a trust value and not a flag - don't know why * I initially designed it that way. */ if (main_pk->has_expired || pk->has_expired) validity = ((validity & (~TRUST_MASK | TRUST_FLAG_PENDING_CHECK)) | TRUST_EXPIRED); - if (main_pk != pk) + if (main_pk != pk && !kb) free_public_key (main_pk); return validity; } int -get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) +get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, + PKT_user_id *uid) { int trustlevel; + if (kb && pk) + log_assert (keyid_cmp (pk_main_keyid (pk), + pk_main_keyid (kb->pkt->pkt.public_key)) == 0); + + if (! pk && kb) + pk = kb->pkt->pkt.public_key; if (!pk) return '?'; /* Just in case a NULL PK is passed. */ - trustlevel = get_validity (ctrl, pk, uid, NULL, 0); + trustlevel = get_validity (ctrl, kb, pk, uid, NULL, 0); if ((trustlevel & TRUST_FLAG_REVOKED)) return 'r'; return trust_letter (trustlevel); } const char * get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) { int trustlevel; if (!pk) return "err"; /* Just in case a NULL PK is passed. */ - trustlevel = get_validity (ctrl, pk, uid, NULL, 0); + trustlevel = get_validity (ctrl, NULL, pk, uid, NULL, 0); if ((trustlevel & TRUST_FLAG_REVOKED)) return _("revoked"); return trust_value_to_string (trustlevel); } /* * Mark the signature of the given UID which are used to certify it. * To do this, we first revmove all signatures which are not valid and * from the remain ones we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting * node flag bit 8. Revocations are marked with flag 11, and sigs * from unavailable keys are marked with flag 12. Note that flag bits * 9 and 10 are used for internal purposes. */ void mark_usable_uid_certs (kbnode_t keyblock, kbnode_t uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire) { kbnode_t node; PKT_signature *sig; /* First check all signatures. */ for (node=uidnode->next; node; node = node->next) { int rc; node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype != PKT_SIGNATURE) continue; sig = node->pkt->pkt.signature; if (main_kid && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) continue; /* ignore self-signatures if we pass in a main_kid */ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) continue; /* we only look at these signature classes */ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && sig->sig_class-0x10flag |= 1<<12; continue; } node->flag |= 1<<9; } /* Reset the remaining flags. */ for (; node; node = node->next) node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); /* kbnode flag usage: bit 9 is here set for signatures to consider, * bit 10 will be set by the loop to keep track of keyIDs already * processed, bit 8 will be set for the usable signatures, and bit * 11 will be set for usable revocations. */ /* For each cert figure out the latest valid one. */ for (node=uidnode->next; node; node = node->next) { KBNODE n, signode; u32 kid[2]; u32 sigdate; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(node->flag & (1<<9)) ) continue; /* not a node to look at */ if ( (node->flag & (1<<10)) ) continue; /* signature with a keyID already processed */ node->flag |= (1<<10); /* mark this node as processed */ sig = node->pkt->pkt.signature; signode = node; sigdate = sig->timestamp; kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; /* Now find the latest and greatest signature */ for (n=uidnode->next; n; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(n->flag & (1<<9)) ) continue; if ( (n->flag & (1<<10)) ) continue; /* shortcut already processed signatures */ sig = n->pkt->pkt.signature; if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) continue; n->flag |= (1<<10); /* mark this node as processed */ /* If signode is nonrevocable and unexpired and n isn't, then take signode (skip). It doesn't matter which is older: if signode was older then we don't want to take n as signode is nonrevocable. If n was older then we're automatically fine. */ if(((IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && (!(IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) continue; /* If n is nonrevocable and unexpired and signode isn't, then take n. Again, it doesn't matter which is older: if n was older then we don't want to take signode as n is nonrevocable. If signode was older then we're automatically fine. */ if((!(IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && ((IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) { signode = n; sigdate = sig->timestamp; continue; } /* At this point, if it's newer, it goes in as the only remaining possibilities are signode and n are both either revocable or expired or both nonrevocable and unexpired. If the timestamps are equal take the later ordered packet, presuming that the key packets are hopefully in their original order. */ if (sig->timestamp >= sigdate) { signode = n; sigdate = sig->timestamp; } } sig = signode->pkt->pkt.signature; if (IS_UID_SIG (sig)) { /* this seems to be a usable one which is not revoked. * Just need to check whether there is an expiration time, * We do the expired certification after finding a suitable * certification, the assumption is that a signator does not * want that after the expiration of his certificate the * system falls back to an older certification which has a * different expiration time */ const byte *p; u32 expire; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); expire = p? sig->timestamp + buf32_to_u32(p) : 0; if (expire==0 || expire > curtime ) { signode->flag |= (1<<8); /* yeah, found a good cert */ if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } else signode->flag |= (1<<11); } } static int clean_sigs_from_uid (kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only) { int deleted = 0; kbnode_t node; u32 keyid[2]; log_assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); /* Passing in a 0 for current time here means that we'll never weed out an expired sig. This is correct behavior since we want to keep the most recent expired sig in a series. */ mark_usable_uid_certs (keyblock, uidnode, NULL, NULL, 0, NULL); /* What we want to do here is remove signatures that are not considered as part of the trust calculations. Thus, all invalid signatures are out, as are any signatures that aren't the last of a series of uid sigs or revocations It breaks down like this: coming out of mark_usable_uid_certs, if a sig is unflagged, it is not even a candidate. If a sig has flag 9 or 10, that means it was selected as a candidate and vetted. If a sig has flag 8 it is a usable signature. If a sig has flag 11 it is a usable revocation. If a sig has flag 12 it was issued by an unavailable key. "Usable" here means the most recent valid signature/revocation in a series from a particular signer. Delete everything that isn't a usable uid sig (which might be expired), a usable revocation, or a sig from an unavailable key. */ for (node=uidnode->next; node && node->pkt->pkttype==PKT_SIGNATURE; node=node->next) { int keep; keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; /* Keep usable uid sigs ... */ if ((node->flag & (1<<8)) && keep) continue; /* ... and usable revocations... */ if ((node->flag & (1<<11)) && keep) continue; /* ... and sigs from unavailable keys. */ /* disabled for now since more people seem to want sigs from unavailable keys removed altogether. */ /* if(node->flag & (1<<12)) continue; */ /* Everything else we delete */ /* At this point, if 12 is set, the signing key was unavailable. If 9 or 10 is set, it's superseded. Otherwise, it's invalid. */ if (noisy) log_info ("removing signature from key %s on user ID \"%s\": %s\n", keystr (node->pkt->pkt.signature->keyid), uidnode->pkt->pkt.user_id->name, node->flag&(1<<12)? "key unavailable": node->flag&(1<<9)? "signature superseded" /* */ :"invalid signature" ); delete_kbnode (node); deleted++; } return deleted; } /* This is substantially easier than clean_sigs_from_uid since we just have to establish if the uid has a valid self-sig, is not revoked, and is not expired. Note that this does not take into account whether the uid has a trust path to it - just whether the keyholder themselves has certified the uid. Returns true if the uid was compacted. To "compact" a user ID, we simply remove ALL signatures except the self-sig that caused the user ID to be remove-worthy. We don't actually remove the user ID packet itself since it might be resurrected in a later merge. Note that this function requires that the caller has already done a merge_keys_and_selfsig(). TODO: change the import code to allow importing a uid with only a revocation if the uid already exists on the keyring. */ static int clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) { kbnode_t node; PKT_user_id *uid = uidnode->pkt->pkt.user_id; int deleted = 0; log_assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); log_assert (uidnode->pkt->pkttype==PKT_USER_ID); /* Skip valid user IDs, compacted user IDs, and non-self-signed user IDs if --allow-non-selfsigned-uid is set. */ if (uid->created || uid->flags.compacted || (!uid->is_expired && !uid->is_revoked && opt.allow_non_selfsigned_uid)) return 0; for (node=uidnode->next; node && node->pkt->pkttype == PKT_SIGNATURE; node=node->next) { if (!node->pkt->pkt.signature->flags.chosen_selfsig) { delete_kbnode (node); deleted = 1; uidnode->pkt->pkt.user_id->flags.compacted = 1; } } if (noisy) { const char *reason; char *user = utf8_to_native (uid->name, uid->len, 0); if (uid->is_revoked) reason = _("revoked"); else if (uid->is_expired) reason = _("expired"); else reason = _("invalid"); log_info ("compacting user ID \"%s\" on key %s: %s\n", user, keystr_from_pk (keyblock->pkt->pkt.public_key), reason); xfree (user); } return deleted; } /* Needs to be called after a merge_keys_and_selfsig() */ void clean_one_uid (kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) { int dummy = 0; log_assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY); log_assert (uidnode->pkt->pkttype==PKT_USER_ID); if (!uids_cleaned) uids_cleaned = &dummy; if (!sigs_cleaned) sigs_cleaned = &dummy; /* Do clean_uid_from_key first since if it fires off, we don't have to bother with the other. */ *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); if (!uidnode->pkt->pkt.user_id->flags.compacted) *sigs_cleaned += clean_sigs_from_uid (keyblock, uidnode, noisy, self_only); } void clean_key (kbnode_t keyblock, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) { kbnode_t uidnode; merge_keys_and_selfsig (keyblock); for (uidnode = keyblock->next; uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY; uidnode = uidnode->next) { if (uidnode->pkt->pkttype == PKT_USER_ID) clean_one_uid (keyblock, uidnode,noisy, self_only, uids_cleaned, sigs_cleaned); } } diff --git a/g10/trustdb.c b/g10/trustdb.c index 51a8f2217..d402cb2ba 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,2184 +1,2204 @@ /* trustdb.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #ifndef DISABLE_REGEX #include #include #endif /* !DISABLE_REGEX */ #include "gpg.h" #include "status.h" #include "iobuf.h" #include "keydb.h" #include "util.h" #include "options.h" #include "packet.h" #include "main.h" #include "mbox-util.h" #include "i18n.h" #include "tdbio.h" #include "trustdb.h" #include "tofu.h" typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ /* * Structure to keep track of keys, this is used as an array wherre * the item right after the last one has a keyblock set to NULL. * Maybe we can drop this thing and replace it by key_item */ struct key_array { KBNODE keyblock; }; /* Control information for the trust DB. */ static struct { int init; int level; char *dbname; int no_trustdb; } trustdb_args; /* Some globals. */ static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */ static struct key_item *utk_list; /* all ultimately trusted keys */ static int pending_check_trustdb; static int validate_keys (ctrl_t ctrl, int interactive); /********************************************** ************* some helpers ******************* **********************************************/ static struct key_item * new_key_item (void) { struct key_item *k; k = xmalloc_clear (sizeof *k); return k; } static void release_key_items (struct key_item *k) { struct key_item *k2; for (; k; k = k2) { k2 = k->next; xfree (k->trust_regexp); xfree (k); } } #define KEY_HASH_TABLE_SIZE 1024 /* * For fast keylook up we need a hash table. Each byte of a KeyID * should be distributed equally over the 256 possible values (except * for v3 keyIDs but we consider them as not important here). So we * can just use 10 bits to index a table of KEY_HASH_TABLE_SIZE key items. * Possible optimization: Do not use key_items but other hash_table when the * duplicates lists get too large. */ static KeyHashTable new_key_hash_table (void) { struct key_item **tbl; tbl = xmalloc_clear (KEY_HASH_TABLE_SIZE * sizeof *tbl); return tbl; } static void release_key_hash_table (KeyHashTable tbl) { int i; if (!tbl) return; for (i=0; i < KEY_HASH_TABLE_SIZE; i++) release_key_items (tbl[i]); xfree (tbl); } /* * Returns: True if the keyID is in the given hash table */ static int test_key_hash_table (KeyHashTable tbl, u32 *kid) { struct key_item *k; for (k = tbl[(kid[1] % KEY_HASH_TABLE_SIZE)]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return 1; return 0; } /* * Add a new key to the hash table. The key is identified by its key ID. */ static void add_key_hash_table (KeyHashTable tbl, u32 *kid) { int i = kid[1] % KEY_HASH_TABLE_SIZE; struct key_item *k, *kk; for (k = tbl[i]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; /* already in table */ kk = new_key_item (); kk->kid[0] = kid[0]; kk->kid[1] = kid[1]; kk->next = tbl[i]; tbl[i] = kk; } /* * Release a key_array */ static void release_key_array ( struct key_array *keys ) { struct key_array *k; if (keys) { for (k=keys; k->keyblock; k++) release_kbnode (k->keyblock); xfree (keys); } } /********************************************* ********** Initialization ***************** *********************************************/ /* * Used to register extra ultimately trusted keys - this has to be done * before initializing the validation module. * FIXME: Should be replaced by a function to add those keys to the trustdb. */ void tdb_register_trusted_keyid (u32 *keyid) { struct key_item *k; k = new_key_item (); k->kid[0] = keyid[0]; k->kid[1] = keyid[1]; k->next = user_utk_list; user_utk_list = k; } void tdb_register_trusted_key( const char *string ) { gpg_error_t err; KEYDB_SEARCH_DESC desc; err = classify_user_id (string, &desc, 1); if (err || desc.mode != KEYDB_SEARCH_MODE_LONG_KID ) { log_error(_("'%s' is not a valid long keyID\n"), string ); return; } register_trusted_keyid(desc.u.kid); } /* * Helper to add a key to the global list of ultimately trusted keys. * Retruns: true = inserted, false = already in in list. */ static int add_utk (u32 *kid) { struct key_item *k; if (tdb_keyid_is_utk (kid)) return 0; k = new_key_item (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->ownertrust = TRUST_ULTIMATE; k->next = utk_list; utk_list = k; if( opt.verbose > 1 ) log_info(_("key %s: accepted as trusted key\n"), keystr(kid)); return 1; } /**************** * Verify that all our secret keys are usable and put them into the utk_list. */ static void verify_own_keys(void) { TRUSTREC rec; ulong recnum; int rc; struct key_item *k; if (utk_list) return; /* scan the trustdb to find all ultimately trusted keys */ for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if ( rec.rectype == RECTYPE_TRUST && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE) { byte *fpr = rec.r.trust.fingerprint; int fprlen; u32 kid[2]; /* Problem: We do only use fingerprints in the trustdb but * we need the keyID here to indetify the key; we can only * use that ugly hack to distinguish between 16 and 20 * butes fpr - it does not work always so we better change * the whole validation code to only work with * fingerprints */ fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; keyid_from_fingerprint (fpr, fprlen, kid); if (!add_utk (kid)) log_info(_("key %s occurs more than once in the trustdb\n"), keystr(kid)); } } /* Put any --trusted-key keys into the trustdb */ for (k = user_utk_list; k; k = k->next) { if ( add_utk (k->kid) ) { /* not yet in trustDB as ultimately trusted */ PKT_public_key pk; memset (&pk, 0, sizeof pk); rc = get_pubkey (&pk, k->kid); if (rc) log_info(_("key %s: no public key for trusted key - skipped\n"), keystr(k->kid)); else { tdb_update_ownertrust (&pk, ((tdb_get_ownertrust (&pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); release_public_key_parts (&pk); } log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid)); } } /* release the helper table table */ release_key_items (user_utk_list); user_utk_list = NULL; return; } /* Returns whether KID is on the list of ultimately trusted keys. */ int tdb_keyid_is_utk (u32 *kid) { struct key_item *k; for (k = utk_list; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return 1; return 0; } /* Return the list of ultimately trusted keys. */ struct key_item * tdb_utks (void) { return utk_list; } /********************************************* *********** TrustDB stuff ******************* *********************************************/ /* * Read a record but die if it does not exist */ static void read_record (ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record (recno, rec, rectype); if (rc) { log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rec->rectype, gpg_strerror (rc) ); tdbio_invalid(); } if (rectype != rec->rectype) { log_error(_("trust record %lu is not of requested type %d\n"), rec->recnum, rectype); tdbio_invalid(); } } /* * Write a record and die on error */ static void write_record (TRUSTREC *rec) { int rc = tdbio_write_record (rec); if (rc) { log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, gpg_strerror (rc) ); tdbio_invalid(); } } /* * sync the TrustDb and die on error */ static void do_sync(void) { int rc = tdbio_sync (); if(rc) { log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) ); g10_exit(2); } } const char * trust_model_string (int model) { switch (model) { case TM_CLASSIC: return "classic"; case TM_PGP: return "pgp"; case TM_EXTERNAL: return "external"; case TM_TOFU: return "tofu"; case TM_TOFU_PGP: return "tofu+pgp"; case TM_ALWAYS: return "always"; case TM_DIRECT: return "direct"; default: return "unknown"; } } /**************** * Perform some checks over the trustdb * level 0: only open the db * 1: used for initial program startup */ int setup_trustdb( int level, const char *dbname ) { /* just store the args */ if( trustdb_args.init ) return 0; trustdb_args.level = level; trustdb_args.dbname = dbname? xstrdup(dbname): NULL; return 0; } void how_to_fix_the_trustdb () { const char *name = trustdb_args.dbname; if (!name) name = "trustdb.gpg"; log_info (_("You may try to re-create the trustdb using the commands:\n")); log_info (" cd %s\n", default_homedir ()); log_info (" %s --export-ownertrust > otrust.tmp\n", GPG_NAME); #ifdef HAVE_W32_SYSTEM log_info (" del %s\n", name); #else log_info (" rm %s\n", name); #endif log_info (" %s --import-ownertrust < otrust.tmp\n", GPG_NAME); log_info (_("If that does not work, please consult the manual\n")); } void init_trustdb () { int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; if( trustdb_args.init ) return; trustdb_args.init = 1; if(level==0 || level==1) { int rc = tdbio_set_dbname( dbname, !!level, &trustdb_args.no_trustdb); if( rc ) log_fatal("can't init trustdb: %s\n", gpg_strerror (rc) ); } else BUG(); if(opt.trust_model==TM_AUTO) { /* Try and set the trust model off of whatever the trustdb says it is. */ opt.trust_model=tdbio_read_model(); /* Sanity check this ;) */ if(opt.trust_model != TM_CLASSIC && opt.trust_model != TM_PGP && opt.trust_model != TM_TOFU_PGP && opt.trust_model != TM_TOFU && opt.trust_model != TM_EXTERNAL) { log_info(_("unable to use unknown trust model (%d) - " "assuming %s trust model\n"),opt.trust_model,"pgp"); opt.trust_model = TM_PGP; } if(opt.verbose) log_info(_("using %s trust model\n"), trust_model_string (opt.trust_model)); } if (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC || opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { /* Verify the list of ultimately trusted keys and move the --trusted-keys list there as well. */ if(level==1) verify_own_keys(); if(!tdbio_db_matches_options()) pending_check_trustdb=1; } } /**************** * Recreate the WoT but do not ask for new ownertrusts. Special * feature: In batch mode and without a forced yes, this is only done * when a check is due. This can be used to run the check from a crontab */ void check_trustdb (ctrl_t ctrl) { init_trustdb(); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU) { if (opt.batch && !opt.answer_yes) { ulong scheduled; scheduled = tdbio_read_nextcheck (); if (!scheduled) { log_info (_("no need for a trustdb check\n")); return; } if (scheduled > make_timestamp ()) { log_info (_("next trustdb check due at %s\n"), strtimestamp (scheduled)); return; } } validate_keys (ctrl, 0); } else log_info (_("no need for a trustdb check with '%s' trust model\n"), trust_model_string(opt.trust_model)); } /* * Recreate the WoT. */ void update_trustdb (ctrl_t ctrl) { init_trustdb (); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU) validate_keys (ctrl, 1); else log_info (_("no need for a trustdb update with '%s' trust model\n"), trust_model_string(opt.trust_model)); } void tdb_revalidation_mark (void) { init_trustdb(); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; /* We simply set the time for the next check to 1 (far back in 1970) so that a --update-trustdb will be scheduled. */ if (tdbio_write_nextcheck (1)) do_sync (); pending_check_trustdb = 1; } int trustdb_pending_check(void) { return pending_check_trustdb; } /* If the trustdb is dirty, and we're interactive, update it. Otherwise, check it unless no-auto-check-trustdb is set. */ void tdb_check_or_update (ctrl_t ctrl) { if (trustdb_pending_check ()) { if (opt.interactive) update_trustdb (ctrl); else if (!opt.no_auto_check_trustdb) check_trustdb (ctrl); } } void read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, byte *marginals,byte *completes,byte *cert_depth, byte *min_cert_level) { TRUSTREC opts; init_trustdb(); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) memset (&opts, 0, sizeof opts); else read_record (0, &opts, RECTYPE_VER); if(trust_model) *trust_model=opts.r.ver.trust_model; if(created) *created=opts.r.ver.created; if(nextcheck) *nextcheck=opts.r.ver.nextcheck; if(marginals) *marginals=opts.r.ver.marginals; if(completes) *completes=opts.r.ver.completes; if(cert_depth) *cert_depth=opts.r.ver.cert_depth; if(min_cert_level) *min_cert_level=opts.r.ver.min_cert_level; } /*********************************************** *********** Ownertrust et al. **************** ***********************************************/ static int read_trust_record (PKT_public_key *pk, TRUSTREC *rec) { int rc; init_trustdb(); rc = tdbio_search_trust_bypk (pk, rec); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("trustdb: searching trust record failed: %s\n", gpg_strerror (rc)); return rc; } if (rec->rectype != RECTYPE_TRUST) { log_error ("trustdb: record %lu is not a trust record\n", rec->recnum); return GPG_ERR_TRUSTDB; } return 0; } /**************** * Return the assigned ownertrust value for the given public key. * The key should be the primary key. */ unsigned int tdb_get_ownertrust ( PKT_public_key *pk) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; err = read_trust_record (pk, &rec); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) return TRUST_UNKNOWN; /* no record yet */ if (err) { tdbio_invalid (); return TRUST_UNKNOWN; /* actually never reached */ } return rec.r.trust.ownertrust; } unsigned int tdb_get_min_ownertrust (PKT_public_key *pk) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; err = read_trust_record (pk, &rec); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) return TRUST_UNKNOWN; /* no record yet */ if (err) { tdbio_invalid (); return TRUST_UNKNOWN; /* actually never reached */ } return rec.r.trust.min_ownertrust; } /* * Set the trust value of the given public key to the new value. * The key should be a primary one. */ void tdb_update_ownertrust (PKT_public_key *pk, unsigned int new_trust ) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; err = read_trust_record (pk, &rec); if (!err) { if (DBG_TRUST) log_debug ("update ownertrust from %u to %u\n", (unsigned int)rec.r.trust.ownertrust, new_trust ); if (rec.r.trust.ownertrust != new_trust) { rec.r.trust.ownertrust = new_trust; write_record( &rec ); tdb_revalidation_mark (); do_sync (); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* no record yet - create a new one */ size_t dummy; if (DBG_TRUST) log_debug ("insert ownertrust %u\n", new_trust ); memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (); rec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy); rec.r.trust.ownertrust = new_trust; write_record (&rec); tdb_revalidation_mark (); do_sync (); } else { tdbio_invalid (); } } static void update_min_ownertrust (u32 *kid, unsigned int new_trust ) { PKT_public_key *pk; TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; pk = xmalloc_clear (sizeof *pk); err = get_pubkey (pk, kid); if (err) { log_error (_("public key %s not found: %s\n"), keystr (kid), gpg_strerror (err)); return; } err = read_trust_record (pk, &rec); if (!err) { if (DBG_TRUST) log_debug ("key %08lX%08lX: update min_ownertrust from %u to %u\n", (ulong)kid[0],(ulong)kid[1], (unsigned int)rec.r.trust.min_ownertrust, new_trust ); if (rec.r.trust.min_ownertrust != new_trust) { rec.r.trust.min_ownertrust = new_trust; write_record( &rec ); tdb_revalidation_mark (); do_sync (); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* no record yet - create a new one */ size_t dummy; if (DBG_TRUST) log_debug ("insert min_ownertrust %u\n", new_trust ); memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (); rec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy); rec.r.trust.min_ownertrust = new_trust; write_record (&rec); tdb_revalidation_mark (); do_sync (); } else { tdbio_invalid (); } } /* * Clear the ownertrust and min_ownertrust values. * * Return: True if a change actually happened. */ int tdb_clear_ownertrusts (PKT_public_key *pk) { TRUSTREC rec; gpg_error_t err; init_trustdb (); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return 0; err = read_trust_record (pk, &rec); if (!err) { if (DBG_TRUST) { log_debug ("clearing ownertrust (old value %u)\n", (unsigned int)rec.r.trust.ownertrust); log_debug ("clearing min_ownertrust (old value %u)\n", (unsigned int)rec.r.trust.min_ownertrust); } if (rec.r.trust.ownertrust || rec.r.trust.min_ownertrust) { rec.r.trust.ownertrust = 0; rec.r.trust.min_ownertrust = 0; write_record( &rec ); tdb_revalidation_mark (); do_sync (); return 1; } } else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); } return 0; } /* * Note: Caller has to do a sync */ static void update_validity (PKT_public_key *pk, PKT_user_id *uid, int depth, int validity) { TRUSTREC trec, vrec; gpg_error_t err; ulong recno; namehash_from_uid(uid); err = read_trust_record (pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); return; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record yet - create a new one. */ size_t dummy; memset (&trec, 0, sizeof trec); trec.recnum = tdbio_new_recnum (); trec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy); trec.r.trust.ownertrust = 0; } /* locate an existing one */ recno = trec.r.trust.validlist; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if ( !memcmp (vrec.r.valid.namehash, uid->namehash, 20) ) break; recno = vrec.r.valid.next; } if (!recno) /* insert a new validity record */ { memset (&vrec, 0, sizeof vrec); vrec.recnum = tdbio_new_recnum (); vrec.rectype = RECTYPE_VALID; memcpy (vrec.r.valid.namehash, uid->namehash, 20); vrec.r.valid.next = trec.r.trust.validlist; trec.r.trust.validlist = vrec.recnum; } vrec.r.valid.validity = validity; vrec.r.valid.full_count = uid->help_full_count; vrec.r.valid.marginal_count = uid->help_marginal_count; write_record (&vrec); trec.r.trust.depth = depth; write_record (&trec); } /*********************************************** ********* Query trustdb values ************** ***********************************************/ /* Return true if key is disabled. Note that this is usually used via the pk_is_disabled macro. */ int tdb_cache_disabled_value (PKT_public_key *pk) { gpg_error_t err; TRUSTREC trec; int disabled = 0; if (pk->flags.disabled_valid) return pk->flags.disabled; init_trustdb(); if (trustdb_args.no_trustdb) return 0; /* No trustdb => not disabled. */ err = read_trust_record (pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); goto leave; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record found, so assume not disabled. */ goto leave; } if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)) disabled = 1; /* Cache it for later so we don't need to look at the trustdb every time */ pk->flags.disabled = disabled; pk->flags.disabled_valid = 1; leave: return disabled; } void tdb_check_trustdb_stale (ctrl_t ctrl) { static int did_nextcheck=0; init_trustdb (); if (trustdb_args.no_trustdb) return; /* No trustdb => can't be stale. */ if (!did_nextcheck && (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU)) { ulong scheduled; did_nextcheck = 1; scheduled = tdbio_read_nextcheck (); if ((scheduled && scheduled <= make_timestamp ()) || pending_check_trustdb) { if (opt.no_auto_check_trustdb) { pending_check_trustdb = 1; if (!opt.quiet) log_info (_("please do a --check-trustdb\n")); } else { if (!opt.quiet) log_info (_("checking the trustdb\n")); validate_keys (ctrl, 0); } } } } /* - * Return the validity information for PK. This is the core of - * get_validity. If SIG is not NULL, then the trust is being - * evaluated in the context of the provided signature. This is used - * by the TOFU code to record statistics. + * Return the validity information for KB/PK (at least one of them + * must be non-NULL). This is the core of get_validity. If SIG is + * not NULL, then the trust is being evaluated in the context of the + * provided signature. This is used by the TOFU code to record + * statistics. */ unsigned int tdb_get_validity_core (ctrl_t ctrl, + kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *main_pk, PKT_signature *sig, int may_ask) { TRUSTREC trec, vrec; gpg_error_t err = 0; ulong recno; #ifdef USE_TOFU unsigned int tofu_validity = TRUST_UNKNOWN; #endif unsigned int validity = TRUST_UNKNOWN; + int free_kb = 0; + + if (kb && pk) + log_assert (keyid_cmp (pk_main_keyid (pk), + pk_main_keyid (kb->pkt->pkt.public_key)) == 0); + + if (! pk) + { + log_assert (kb); + pk = kb->pkt->pkt.public_key; + } #ifndef USE_TOFU (void)sig; (void)may_ask; #endif init_trustdb (); /* If we have no trustdb (which also means it has not been created) and the trust-model is always, we don't know the validity - return immediately. If we won't do that the tdbio code would try to open the trustdb and run into a fatal error. */ if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; check_trustdb_stale (ctrl); if(opt.trust_model==TM_DIRECT) { /* Note that this happens BEFORE any user ID stuff is checked. The direct trust model applies to keys as a whole. */ validity = tdb_get_ownertrust (main_pk); goto leave; } #ifdef USE_TOFU if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { - kbnode_t kb = NULL; kbnode_t n = NULL; strlist_t user_id_list = NULL; int done = 0; /* If the caller didn't supply a user id then use all uids. */ if (! uid) - kb = n = get_pubkeyblock (main_pk->keyid); + { + if (! kb) + { + kb = get_pubkeyblock (main_pk->keyid); + free_kb = 1; + } + n = kb; + } if (DBG_TRUST && sig && sig->signers_uid) log_debug ("TOFU: only considering user id: '%s'\n", sig->signers_uid); while (!done && (uid || (n = find_next_kbnode (n, PKT_USER_ID)))) { PKT_user_id *user_id; int expired = 0; if (uid) { user_id = uid; /* If the caller specified a user id, then we only process the specified user id and are done after the first iteration. */ done = 1; } else user_id = n->pkt->pkt.user_id; if (user_id->attrib_data) /* Skip user attributes. */ continue; if (sig && sig->signers_uid) /* Make sure the UID matches. */ { char *email = mailbox_from_userid (user_id->name); if (!email || !*email || strcmp (sig->signers_uid, email) != 0) { if (DBG_TRUST) log_debug ("TOFU: skipping user id '%s', which does" " not match the signer's email ('%s')\n", email, sig->signers_uid); xfree (email); continue; } xfree (email); } /* If the user id is revoked or expired, then skip it. */ if (user_id->is_revoked || user_id->is_expired) { if (DBG_TRUST) { char *s; if (user_id->is_revoked && user_id->is_expired) s = "revoked and expired"; else if (user_id->is_revoked) s = "revoked"; else s = "expire"; log_debug ("TOFU: Ignoring %s user id (%s)\n", s, user_id->name); } if (user_id->is_revoked) continue; expired = 1; } add_to_strlist (&user_id_list, user_id->name); user_id_list->flags = expired; } /* Process the user ids in the order they appear in the key block. */ strlist_rev (&user_id_list); /* It only makes sense to observe any signature before getting the validity. This is because if the current signature results in a conflict, then we damn well want to take that into account. */ if (sig) { err = tofu_register_signature (ctrl, main_pk, user_id_list, sig->digest, sig->digest_len, sig->timestamp, "unknown"); if (err) { log_error ("TOFU: error registering signature: %s\n", gpg_strerror (err)); tofu_validity = TRUST_UNKNOWN; } } if (! err) tofu_validity = tofu_get_validity (ctrl, main_pk, user_id_list, may_ask); free_strlist (user_id_list); - release_kbnode (kb); + if (free_kb) + release_kbnode (kb); } #endif /*USE_TOFU*/ if (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_PGP) { err = read_trust_record (main_pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); return 0; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record found. */ validity = TRUST_UNKNOWN; goto leave; } /* Loop over all user IDs */ recno = trec.r.trust.validlist; validity = 0; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(uid) { /* If a user ID is given we return the validity for that user ID ONLY. If the namehash is not found, then there is no validity at all (i.e. the user ID wasn't signed). */ if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { validity=(vrec.r.valid.validity & TRUST_MASK); break; } } else { /* If no user ID is given, we take the maximum validity over all user IDs */ if (validity < (vrec.r.valid.validity & TRUST_MASK)) validity = (vrec.r.valid.validity & TRUST_MASK); } recno = vrec.r.valid.next; } if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)) { validity |= TRUST_FLAG_DISABLED; pk->flags.disabled = 1; } else pk->flags.disabled = 0; pk->flags.disabled_valid = 1; } leave: #ifdef USE_TOFU validity = tofu_wot_trust_combine (tofu_validity, validity); #else /*!USE_TOFU*/ validity &= TRUST_MASK; if (validity == TRUST_NEVER) /* TRUST_NEVER trumps everything else. */ validity |= TRUST_NEVER; if (validity == TRUST_EXPIRED) /* TRUST_EXPIRED trumps everything but TRUST_NEVER. */ validity |= TRUST_EXPIRED; #endif /*!USE_TOFU*/ if (opt.trust_model != TM_TOFU && pending_check_trustdb) validity |= TRUST_FLAG_PENDING_CHECK; return validity; } static void get_validity_counts (PKT_public_key *pk, PKT_user_id *uid) { TRUSTREC trec, vrec; ulong recno; if(pk==NULL || uid==NULL) BUG(); namehash_from_uid(uid); uid->help_marginal_count=uid->help_full_count=0; init_trustdb (); if(read_trust_record (pk, &trec)) return; /* loop over all user IDs */ recno = trec.r.trust.validlist; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { uid->help_marginal_count=vrec.r.valid.marginal_count; uid->help_full_count=vrec.r.valid.full_count; /* es_printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */ break; } recno = vrec.r.valid.next; } } void list_trust_path( const char *username ) { (void)username; } /**************** * Enumerate all keys, which are needed to build all trust paths for * the given key. This function does not return the key itself or * the ultimate key (the last point in cerificate chain). Only * certificate chains which ends up at an ultimately trusted key * are listed. If ownertrust or validity is not NULL, the corresponding * value for the returned LID is also returned in these variable(s). * * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function. * Set lid to the key you want to enumerate and pass it by reference. * 3) call this function as long as it does not return -1 * to indicate EOF. LID does contain the next key used to build the web * 4) Always call this function a last time with LID set to NULL, * so that it can free its context. * * Returns: -1 on EOF or the level of the returned LID */ int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ) { (void)context; (void)lid; (void)ownertrust; (void)validity; return -1; } /**************** * Print the current path */ void enum_cert_paths_print (void **context, FILE *fp, int refresh, ulong selected_lid) { (void)context; (void)fp; (void)refresh; (void)selected_lid; } /**************************************** *********** NEW NEW NEW **************** ****************************************/ static int ask_ownertrust (ctrl_t ctrl, u32 *kid, int minimum) { PKT_public_key *pk; int rc; int ot; pk = xmalloc_clear (sizeof *pk); rc = get_pubkey (pk, kid); if (rc) { log_error (_("public key %s not found: %s\n"), keystr(kid), gpg_strerror (rc) ); return TRUST_UNKNOWN; } if(opt.force_ownertrust) { log_info("force trust for key %s to %s\n", keystr(kid),trust_value_to_string(opt.force_ownertrust)); tdb_update_ownertrust (pk, opt.force_ownertrust); ot=opt.force_ownertrust; } else { ot=edit_ownertrust (ctrl, pk, 0); if(ot>0) ot = tdb_get_ownertrust (pk); else if(ot==0) ot = minimum?minimum:TRUST_UNDEFINED; else ot = -1; /* quit */ } free_public_key( pk ); return ot; } static void mark_keyblock_seen (KeyHashTable tbl, KBNODE node) { for ( ;node; node = node->next ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); add_key_hash_table (tbl, aki); } } static void dump_key_array (int depth, struct key_array *keys) { struct key_array *kar; for (kar=keys; kar->keyblock; kar++) { KBNODE node = kar->keyblock; u32 kid[2]; keyid_from_pk(node->pkt->pkt.public_key, kid); es_printf ("%d:%08lX%08lX:K::%c::::\n", depth, (ulong)kid[0], (ulong)kid[1], '?'); for (; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { int len = node->pkt->pkt.user_id->len; if (len > 30) len = 30; es_printf ("%d:%08lX%08lX:U:::%c:::", depth, (ulong)kid[0], (ulong)kid[1], (node->flag & 4)? 'f': (node->flag & 2)? 'm': (node->flag & 1)? 'q':'-'); es_write_sanitized (es_stdout, node->pkt->pkt.user_id->name, len, ":", NULL); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } } } static void store_validation_status (int depth, KBNODE keyblock, KeyHashTable stored) { KBNODE node; int status; int any = 0; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (node->flag & 4) status = TRUST_FULLY; else if (node->flag & 2) status = TRUST_MARGINAL; else if (node->flag & 1) status = TRUST_UNDEFINED; else status = 0; if (status) { update_validity (keyblock->pkt->pkt.public_key, uid, depth, status); mark_keyblock_seen(stored,keyblock); any = 1; } } } if (any) do_sync (); } /* Returns a sanitized copy of the regexp (which might be "", but not NULL). */ #ifndef DISABLE_REGEX static char * sanitize_regexp(const char *old) { size_t start=0,len=strlen(old),idx=0; int escaped=0,standard_bracket=0; char *new=xmalloc((len*2)+1); /* enough to \-escape everything if we have to */ /* There are basically two commonly-used regexps here. GPG and most versions of PGP use "<[^>]+[@.]example\.com>$" and PGP (9) command line uses "example.com" (i.e. whatever the user specfies, and we can't expect users know to use "\." instead of "."). So here are the rules: we're allowed to start with "<[^>]+[@.]" and end with ">$" or start and end with nothing. In between, the only legal regex character is ".", and everything else gets escaped. Part of the gotcha here is that some regex packages allow more than RFC-4880 requires. For example, 4880 has no "{}" operator, but GNU regex does. Commenting removes these operators from consideration. A possible future enhancement is to use commenting to effectively back off a given regex to the Henry Spencer syntax in 4880. -dshaw */ /* Are we bracketed between "<[^>]+[@.]" and ">$" ? */ if(len>=12 && strncmp(old,"<[^>]+[@.]",10)==0 && old[len-2]=='>' && old[len-1]=='$') { strcpy(new,"<[^>]+[@.]"); idx=strlen(new); standard_bracket=1; start+=10; len-=2; } /* Walk the remaining characters and ensure that everything that is left is not an operational regex character. */ for(;start$", then it was escaping the ">" and is fine. If the regexp actually ended with the bare "\", then it's an illegal regexp and regcomp should kick it out. */ if(standard_bracket) strcat(new,">$"); return new; } #endif /*!DISABLE_REGEX*/ /* Used by validate_one_keyblock to confirm a regexp within a trust signature. Returns 1 for match, and 0 for no match or regex error. */ static int check_regexp(const char *expr,const char *string) { #ifdef DISABLE_REGEX (void)expr; (void)string; /* When DISABLE_REGEX is defined, assume all regexps do not match. */ return 0; #else int ret; char *regexp; regexp=sanitize_regexp(expr); #ifdef __riscos__ ret=riscos_check_regexp(expr, string, DBG_TRUST); #else { regex_t pat; ret=regcomp(&pat,regexp,REG_ICASE|REG_NOSUB|REG_EXTENDED); if(ret==0) { ret=regexec(&pat,string,0,NULL,0); regfree(&pat); ret=(ret==0); } } #endif if(DBG_TRUST) log_debug("regexp '%s' ('%s') on '%s': %s\n", regexp,expr,string,ret==0?"YES":"NO"); xfree(regexp); return ret; #endif } /* * Return true if the key is signed by one of the keys in the given * key ID list. User IDs with a valid signature are marked by node * flags as follows: * flag bit 0: There is at least one signature * 1: There is marginal confidence that this is a legitimate uid * 2: There is full confidence that this is a legitimate uid. * 8: Used for internal purposes. * 9: Ditto (in mark_usable_uid_certs()) * 10: Ditto (ditto) * This function assumes that all kbnode flags are cleared on entry. */ static int validate_one_keyblock (KBNODE kb, struct key_item *klist, u32 curtime, u32 *next_expire) { struct key_item *kr; KBNODE node, uidnode=NULL; PKT_user_id *uid=NULL; PKT_public_key *pk = kb->pkt->pkt.public_key; u32 main_kid[2]; int issigned=0, any_signed = 0; keyid_from_pk(pk, main_kid); for (node=kb; node; node = node->next) { /* A bit of discussion here: is it better for the web of trust to be built among only self-signed uids? On the one hand, a self-signed uid is a statement that the key owner definitely intended that uid to be there, but on the other hand, a signed (but not self-signed) uid does carry trust, of a sort, even if it is a statement being made by people other than the key owner "through" the uids on the key owner's key. I'm going with the latter. However, if the user ID was explicitly revoked, or passively allowed to expire, that should stop validity through the user ID until it is resigned. -dshaw */ if (node->pkt->pkttype == PKT_USER_ID && !node->pkt->pkt.user_id->is_revoked && !node->pkt->pkt.user_id->is_expired) { if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } uidnode = node; uid=uidnode->pkt->pkt.user_id; /* If the selfsig is going to expire... */ if(uid->expiredate && uid->expiredate<*next_expire) *next_expire = uid->expiredate; issigned = 0; get_validity_counts(pk,uid); mark_usable_uid_certs (kb, uidnode, main_kid, klist, curtime, next_expire); } else if (node->pkt->pkttype == PKT_SIGNATURE && (node->flag & (1<<8)) && uid) { /* Note that we are only seeing unrevoked sigs here */ PKT_signature *sig = node->pkt->pkt.signature; kr = is_in_klist (klist, sig); /* If the trust_regexp does not match, it's as if the sig did not exist. This is safe for non-trust sigs as well since we don't accept a regexp on the sig unless it's a trust sig. */ if (kr && (!kr->trust_regexp || !(opt.trust_model == TM_PGP || opt.trust_model == TM_TOFU_PGP) || (uidnode && check_regexp(kr->trust_regexp, uidnode->pkt->pkt.user_id->name)))) { /* Are we part of a trust sig chain? We always favor the latest trust sig, rather than the greater or lesser trust sig or value. I could make a decent argument for any of these cases, but this seems to be what PGP does, and I'd like to be compatible. -dms */ if ((opt.trust_model == TM_PGP || opt.trust_model == TM_TOFU_PGP) && sig->trust_depth && pk->trust_timestamp <= sig->timestamp) { unsigned char depth; /* If the depth on the signature is less than the chain currently has, then use the signature depth so we don't increase the depth beyond what the signer wanted. If the depth on the signature is more than the chain currently has, then use the chain depth so we use as much of the signature depth as the chain will permit. An ultimately trusted signature can restart the depth to whatever level it likes. */ if (sig->trust_depth < kr->trust_depth || kr->ownertrust == TRUST_ULTIMATE) depth = sig->trust_depth; else depth = kr->trust_depth; if (depth) { if(DBG_TRUST) log_debug ("trust sig on %s, sig depth is %d," " kr depth is %d\n", uidnode->pkt->pkt.user_id->name, sig->trust_depth, kr->trust_depth); /* If we got here, we know that: this is a trust sig. it's a newer trust sig than any previous trust sig on this key (not uid). it is legal in that it was either generated by an ultimate key, or a key that was part of a trust chain, and the depth does not violate the original trust sig. if there is a regexp attached, it matched successfully. */ if (DBG_TRUST) log_debug ("replacing trust value %d with %d and " "depth %d with %d\n", pk->trust_value,sig->trust_value, pk->trust_depth,depth); pk->trust_value = sig->trust_value; pk->trust_depth = depth-1; /* If the trust sig contains a regexp, record it on the pk for the next round. */ if (sig->trust_regexp) pk->trust_regexp = sig->trust_regexp; } } if (kr->ownertrust == TRUST_ULTIMATE) uid->help_full_count = opt.completes_needed; else if (kr->ownertrust == TRUST_FULLY) uid->help_full_count++; else if (kr->ownertrust == TRUST_MARGINAL) uid->help_marginal_count++; issigned = 1; } } } if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } return any_signed; } static int search_skipfnc (void *opaque, u32 *kid, int dummy_uid_no) { (void)dummy_uid_no; return test_key_hash_table ((KeyHashTable)opaque, kid); } /* * Scan all keys and return a key_array of all suitable keys from * kllist. The caller has to pass keydb handle so that we don't use * to create our own. Returns either a key_array or NULL in case of * an error. No results found are indicated by an empty array. * Caller hast to release the returned array. */ static struct key_array * validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, struct key_item *klist, u32 curtime, u32 *next_expire) { KBNODE keyblock = NULL; struct key_array *keys = NULL; size_t nkeys, maxkeys; int rc; KEYDB_SEARCH_DESC desc; maxkeys = 1000; keys = xmalloc ((maxkeys+1) * sizeof *keys); nkeys = 0; rc = keydb_search_reset (hd); if (rc) { log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc)); xfree (keys); return NULL; } memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; desc.skipfnc = search_skipfnc; desc.skipfncvalue = full_trust; rc = keydb_search (hd, &desc, 1, NULL); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keys[nkeys].keyblock = NULL; return keys; } if (rc) { log_error ("keydb_search(first) failed: %s\n", gpg_strerror (rc)); goto die; } desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ do { PKT_public_key *pk; rc = keydb_get_keyblock (hd, &keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto die; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { log_debug ("ooops: invalid pkttype %d encountered\n", keyblock->pkt->pkttype); dump_kbnode (keyblock); release_kbnode(keyblock); continue; } /* prepare the keyblock for further processing */ merge_keys_and_selfsig (keyblock); clear_kbnode_flags (keyblock); pk = keyblock->pkt->pkt.public_key; if (pk->has_expired || pk->flags.revoked) { /* it does not make sense to look further at those keys */ mark_keyblock_seen (full_trust, keyblock); } else if (validate_one_keyblock (keyblock, klist, curtime, next_expire)) { KBNODE node; if (pk->expiredate && pk->expiredate >= curtime && pk->expiredate < *next_expire) *next_expire = pk->expiredate; if (nkeys == maxkeys) { maxkeys += 1000; keys = xrealloc (keys, (maxkeys+1) * sizeof *keys); } keys[nkeys++].keyblock = keyblock; /* Optimization - if all uids are fully trusted, then we never need to consider this key as a candidate again. */ for (node=keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4)) break; if(node==NULL) mark_keyblock_seen (full_trust, keyblock); keyblock = NULL; } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search (hd, &desc, 1, NULL))); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) { log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); goto die; } keys[nkeys].keyblock = NULL; return keys; die: keys[nkeys].keyblock = NULL; release_key_array (keys); return NULL; } /* Caller must sync */ static void reset_trust_records(void) { TRUSTREC rec; ulong recnum; int count = 0, nreset = 0; for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if(rec.rectype==RECTYPE_TRUST) { count++; if(rec.r.trust.min_ownertrust) { rec.r.trust.min_ownertrust=0; write_record(&rec); } } else if(rec.rectype==RECTYPE_VALID && ((rec.r.valid.validity&TRUST_MASK) || rec.r.valid.marginal_count || rec.r.valid.full_count)) { rec.r.valid.validity &= ~TRUST_MASK; rec.r.valid.marginal_count=rec.r.valid.full_count=0; nreset++; write_record(&rec); } } if (opt.verbose) { log_info (ngettext("%d key processed", "%d keys processed", count), count); log_printf (ngettext(" (%d validity count cleared)\n", " (%d validity counts cleared)\n", nreset), nreset); } } /* * Run the key validation procedure. * * This works this way: * Step 1: Find all ultimately trusted keys (UTK). * mark them all as seen and put them into klist. * Step 2: loop max_cert_times * Step 3: if OWNERTRUST of any key in klist is undefined * ask user to assign ownertrust * Step 4: Loop over all keys in the keyDB which are not marked seen * Step 5: if key is revoked or expired * mark key as seen * continue loop at Step 4 * Step 6: For each user ID of that key signed by a key in klist * Calculate validity by counting trusted signatures. * Set validity of user ID * Step 7: If any signed user ID was found * mark key as seen * End Loop * Step 8: Build a new klist from all fully trusted keys from step 6 * End Loop * Ready * */ static int validate_keys (ctrl_t ctrl, int interactive) { int rc = 0; int quit=0; struct key_item *klist = NULL; struct key_item *k; struct key_array *keys = NULL; struct key_array *kar; KEYDB_HANDLE kdb = NULL; KBNODE node; int depth; int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; KeyHashTable stored,used,full_trust; u32 start_time, next_expire; /* Make sure we have all sigs cached. TODO: This is going to require some architectural re-thinking, as it is agonizingly slow. Perhaps combine this with reset_trust_records(), or only check the caches on keys that are actually involved in the web of trust. */ keydb_rebuild_caches(0); kdb = keydb_new (); if (!kdb) return gpg_error_from_syserror (); start_time = make_timestamp (); next_expire = 0xffffffff; /* set next expire to the year 2106 */ stored = new_key_hash_table (); used = new_key_hash_table (); full_trust = new_key_hash_table (); reset_trust_records(); /* Fixme: Instead of always building a UTK list, we could just build it * here when needed */ if (!utk_list) { if (!opt.quiet) log_info (_("no ultimately trusted keys found\n")); goto leave; } /* mark all UTKs as used and fully_trusted and set validity to ultimate */ for (k=utk_list; k; k = k->next) { KBNODE keyblock; PKT_public_key *pk; keyblock = get_pubkeyblock (k->kid); if (!keyblock) { log_error (_("public key of ultimately" " trusted key %s not found\n"), keystr(k->kid)); continue; } mark_keyblock_seen (used, keyblock); mark_keyblock_seen (stored, keyblock); mark_keyblock_seen (full_trust, keyblock); pk = keyblock->pkt->pkt.public_key; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) update_validity (pk, node->pkt->pkt.user_id, 0, TRUST_ULTIMATE); } if ( pk->expiredate && pk->expiredate >= start_time && pk->expiredate < next_expire) next_expire = pk->expiredate; release_kbnode (keyblock); do_sync (); } if (opt.trust_model == TM_TOFU) /* In the TOFU trust model, we only need to save the ultimately trusted keys. */ goto leave; klist = utk_list; if (!opt.quiet) log_info ("marginals needed: %d completes needed: %d trust model: %s\n", opt.marginals_needed, opt.completes_needed, trust_model_string (opt.trust_model)); for (depth=0; depth < opt.max_cert_depth; depth++) { int valids=0,key_count; /* See whether we should assign ownertrust values to the keys in klist. */ ot_unknown = ot_undefined = ot_never = 0; ot_marginal = ot_full = ot_ultimate = 0; for (k=klist; k; k = k->next) { int min=0; /* 120 and 60 are as per RFC2440 */ if(k->trust_value>=120) min=TRUST_FULLY; else if(k->trust_value>=60) min=TRUST_MARGINAL; if(min!=k->min_ownertrust) update_min_ownertrust(k->kid,min); if (interactive && k->ownertrust == TRUST_UNKNOWN) { k->ownertrust = ask_ownertrust (ctrl, k->kid,min); if (k->ownertrust == (unsigned int)(-1)) { quit=1; goto leave; } } /* This can happen during transition from an old trustdb before trust sigs. It can also happen if a user uses two different versions of GnuPG or changes the --trust-model setting. */ if(k->ownertrustkid[0],(ulong)k->kid[1], trust_value_to_string(k->ownertrust), trust_value_to_string(min)); k->ownertrust=min; } if (k->ownertrust == TRUST_UNKNOWN) ot_unknown++; else if (k->ownertrust == TRUST_UNDEFINED) ot_undefined++; else if (k->ownertrust == TRUST_NEVER) ot_never++; else if (k->ownertrust == TRUST_MARGINAL) ot_marginal++; else if (k->ownertrust == TRUST_FULLY) ot_full++; else if (k->ownertrust == TRUST_ULTIMATE) ot_ultimate++; valids++; } /* Find all keys which are signed by a key in kdlist */ keys = validate_key_list (kdb, full_trust, klist, start_time, &next_expire); if (!keys) { log_error ("validate_key_list failed\n"); rc = GPG_ERR_GENERAL; goto leave; } for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++) ; /* Store the calculated valididation status somewhere */ if (opt.verbose > 1 && DBG_TRUST) dump_key_array (depth, keys); for (kar=keys; kar->keyblock; kar++) store_validation_status (depth, kar->keyblock, stored); if (!opt.quiet) log_info (_("depth: %d valid: %3d signed: %3d" " trust: %d-, %dq, %dn, %dm, %df, %du\n"), depth, valids, key_count, ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate ); /* Build a new kdlist from all fully valid keys in KEYS */ if (klist != utk_list) release_key_items (klist); klist = NULL; for (kar=keys; kar->keyblock; kar++) { for (node=kar->keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4)) { u32 kid[2]; /* have we used this key already? */ keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid); if(test_key_hash_table(used,kid)==0) { /* Normally we add both the primary and subkey ids to the hash via mark_keyblock_seen, but since we aren't using this hash as a skipfnc, that doesn't matter here. */ add_key_hash_table (used,kid); k = new_key_item (); k->kid[0]=kid[0]; k->kid[1]=kid[1]; k->ownertrust = (tdb_get_ownertrust (kar->keyblock->pkt->pkt.public_key) & TRUST_MASK); k->min_ownertrust = tdb_get_min_ownertrust (kar->keyblock->pkt->pkt.public_key); k->trust_depth= kar->keyblock->pkt->pkt.public_key->trust_depth; k->trust_value= kar->keyblock->pkt->pkt.public_key->trust_value; if(kar->keyblock->pkt->pkt.public_key->trust_regexp) k->trust_regexp= xstrdup(kar->keyblock->pkt-> pkt.public_key->trust_regexp); k->next = klist; klist = k; break; } } } } release_key_array (keys); keys = NULL; if (!klist) break; /* no need to dive in deeper */ } leave: keydb_release (kdb); release_key_array (keys); if (klist != utk_list) release_key_items (klist); release_key_hash_table (full_trust); release_key_hash_table (used); release_key_hash_table (stored); if (!rc && !quit) /* mark trustDB as checked */ { int rc2; if (next_expire == 0xffffffff || next_expire < start_time ) tdbio_write_nextcheck (0); else { tdbio_write_nextcheck (next_expire); if (!opt.quiet) log_info (_("next trustdb check due at %s\n"), strtimestamp (next_expire)); } rc2 = tdbio_update_version_record (); if (rc2) { log_error (_("unable to update trustdb version record: " "write failed: %s\n"), gpg_strerror (rc2)); tdbio_invalid (); } do_sync (); pending_check_trustdb = 0; } return rc; } diff --git a/g10/trustdb.h b/g10/trustdb.h index 45ecc56ab..6081d1088 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -1,169 +1,171 @@ /* trustdb.h - Trust database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, * 2005, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_TRUSTDB_H #define G10_TRUSTDB_H /* Trust values must be sorted in ascending order! */ #define TRUST_MASK 15 #define TRUST_UNKNOWN 0 /* o: not yet calculated/assigned */ #define TRUST_EXPIRED 1 /* e: calculation may be invalid */ #define TRUST_UNDEFINED 2 /* q: not enough information for calculation */ #define TRUST_NEVER 3 /* n: never trust this pubkey */ #define TRUST_MARGINAL 4 /* m: marginally trusted */ #define TRUST_FULLY 5 /* f: fully trusted */ #define TRUST_ULTIMATE 6 /* u: ultimately trusted */ /* Trust values not covered by the mask. */ #define TRUST_FLAG_REVOKED 32 /* r: revoked */ #define TRUST_FLAG_SUB_REVOKED 64 /* r: revoked but for subkeys */ #define TRUST_FLAG_DISABLED 128 /* d: key/uid disabled */ #define TRUST_FLAG_PENDING_CHECK 256 /* a check-trustdb is pending */ #define TRUST_FLAG_TOFU_BASED 512 /* The trust value is based on * the TOFU information. */ /* Private value used in tofu.c - must be different from the trust values. */ #define _tofu_GET_TRUST_ERROR 100 /* Length of the hash used to select UIDs in keyedit.c. */ #define NAMEHASH_LEN 20 /* * A structure to store key identification as well as some stuff needed * for validation */ struct key_item { struct key_item *next; unsigned int ownertrust,min_ownertrust; byte trust_depth; byte trust_value; char *trust_regexp; u32 kid[2]; }; /* * Check whether the signature SIG is in the klist K. */ static inline struct key_item * is_in_klist (struct key_item *k, PKT_signature *sig) { for (; k; k = k->next) { if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) return k; } return NULL; } /*-- trust.c --*/ int cache_disabled_value (PKT_public_key *pk); void register_trusted_keyid (u32 *keyid); void register_trusted_key (const char *string); const char *trust_value_to_string (unsigned int value); int string_to_trust_value (const char *str); const char *uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid); unsigned int get_ownertrust (PKT_public_key *pk); void update_ownertrust (PKT_public_key *pk, unsigned int new_trust); int clear_ownertrusts (PKT_public_key *pk); void revalidation_mark (void); void check_trustdb_stale (ctrl_t ctrl); void check_or_update_trustdb (ctrl_t ctrl); -unsigned int get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid, +unsigned int get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, + PKT_user_id *uid, PKT_signature *sig, int may_ask); -int get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid); +int get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, + PKT_user_id *uid); const char *get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid); void mark_usable_uid_certs (kbnode_t keyblock, kbnode_t uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire); void clean_one_uid (kbnode_t keyblock, kbnode_t uidnode, int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned); void clean_key (kbnode_t keyblock, int noisy, int self_only, int *uids_cleaned,int *sigs_cleaned); /*-- trustdb.c --*/ void tdb_register_trusted_keyid (u32 *keyid); void tdb_register_trusted_key (const char *string); /* Returns whether KID is on the list of ultimately trusted keys. */ int tdb_keyid_is_utk (u32 *kid); /* Return the list of ultimately trusted keys. The caller must not * modify this list nor must it free the list. */ struct key_item *tdb_utks (void); void check_trustdb (ctrl_t ctrl); void update_trustdb (ctrl_t ctrl); int setup_trustdb( int level, const char *dbname ); void how_to_fix_the_trustdb (void); const char *trust_model_string (int model); void init_trustdb( void ); void tdb_check_trustdb_stale (ctrl_t ctrl); void sync_trustdb( void ); void tdb_revalidation_mark (void); int trustdb_pending_check(void); void tdb_check_or_update (ctrl_t ctrl); int tdb_cache_disabled_value (PKT_public_key *pk); -unsigned int tdb_get_validity_core (ctrl_t ctrl, +unsigned int tdb_get_validity_core (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *main_pk, PKT_signature *sig, int may_ask); void list_trust_path( const char *username ); int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ); void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ); void read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, byte *marginals,byte *completes,byte *cert_depth, byte *min_cert_level); unsigned int tdb_get_ownertrust (PKT_public_key *pk); unsigned int tdb_get_min_ownertrust (PKT_public_key *pk); int get_ownertrust_info (PKT_public_key *pk); const char *get_ownertrust_string (PKT_public_key *pk); void tdb_update_ownertrust (PKT_public_key *pk, unsigned int new_trust); int tdb_clear_ownertrusts (PKT_public_key *pk); /*-- tdbdump.c --*/ void list_trustdb (estream_t fp, const char *username); void export_ownertrust(void); void import_ownertrust(const char *fname); /*-- pkclist.c --*/ int edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode); #endif /*G10_TRUSTDB_H*/