Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/g10/getkey.c b/g10/getkey.c
index d9f35a935..084bd654d 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -1,4764 +1,4771 @@
/* 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, 2024 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 <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "options.h"
#include "main.h"
#include "trustdb.h"
#include "../common/i18n.h"
#include "keyserver-internal.h"
#include "call-agent.h"
#include "objcache.h"
#include "../common/host2net.h"
#include "../common/mbox-util.h"
#include "../common/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 criteria. 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;
/* Allow returning an ADSK key. */
int allow_adsk;
/* 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. FWIW: the req_usage field in PKT_public_key
used to be an u8 but meanwhile is an u16. */
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;
/* Hack to return the mechanism (AKL_foo) used to find the key. */
int found_via_akl;
/* 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;
byte fprlen;
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
static void merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock);
static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
kbnode_t *ret_keyblock, kbnode_t *ret_found_key);
static kbnode_t finish_lookup (kbnode_t keyblock,
unsigned int req_usage, int want_exact,
int want_secret, int allow_adsk,
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;
}
/* Disable and drop the public key cache (which is filled by
cache_public_key and get_pubkey). Note: there is currently no way
to re-enable this cache. */
void
getkey_disable_caches (void)
{
#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 ? */
}
/* Free a list of pubkey_t objects. */
void
pubkeys_free (pubkey_t keys)
{
while (keys)
{
pubkey_t next = keys->next;
xfree (keys->pk);
release_kbnode (keys->keyblock);
xfree (keys);
keys = next;
}
}
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);
}
/* Specialized version of get_pubkey which retrieves the key based on
* information in SIG. In contrast to get_pubkey PK is required. If
* FORCED_PK is not NULL, this public key is used and copied to PK.
* If R_KEYBLOCK is not NULL the entire keyblock is stored there if
* found and FORCED_PK is not used; if not used or on error NULL is
* stored there. Use this function only to find the key for
* verification; it can't be used to select a key for signing. */
gpg_error_t
get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig,
PKT_public_key *forced_pk, kbnode_t *r_keyblock)
{
gpg_error_t err;
const byte *fpr;
size_t fprlen;
if (r_keyblock)
*r_keyblock = NULL;
if (forced_pk)
{
copy_public_key (pk, forced_pk);
return 0;
}
/* Make sure to request only keys cabable of signing. This makes
* sure that a subkey w/o a valid backsig or with bad usage flags
* will be skipped. We also request the verification mode so that
* expired and revoked keys are returned. We keep only a requested
* CERT usage in PK for the sake of key signatures. */
pk->req_usage = (PUBKEY_USAGE_SIG | PUBKEY_USAGE_VERIFY
| (pk->req_usage & PUBKEY_USAGE_CERT));
/* First try the ISSUER_FPR info. */
fpr = issuer_fpr_raw (sig, &fprlen);
if (fpr && !get_pubkey_byfpr (ctrl, pk, r_keyblock, fpr, fprlen))
return 0;
if (r_keyblock)
{
release_kbnode (*r_keyblock);
*r_keyblock = NULL;
}
/* Fallback to use the ISSUER_KEYID. */
err = get_pubkey_bykid (ctrl, pk, r_keyblock, sig->keyid);
if (err && r_keyblock)
{
release_kbnode (*r_keyblock);
*r_keyblock = NULL;
}
return err;
}
/* 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!
*
* If R_KEYBLOCK is not NULL, then the first result's keyblock is
* returned in *R_KEYBLOCK. This should be freed using
* release_kbnode().
*
* 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. */
gpg_error_t
get_pubkey_bykid (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
u32 *keyid)
{
int internal = 0;
gpg_error_t rc = 0;
if (r_keyblock)
*r_keyblock = NULL;
#if MAX_PK_CACHE_ENTRIES
if (pk && !r_keyblock)
{
/* 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.
* The old get_pubkey_function did not check PK->REQ_USAGE when
* reading from the cache. This is probably a bug. Note that
* the cache is not used when the caller asked to return the
* entire keyblock. This is because the cache does not
* associate the public key with its primary key. */
pk_cache_entry_t ce;
for (ce = pk_cache; ce; ce = ce->next)
{
if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1])
{
copy_public_key (pk, ce->pk);
return 0;
}
}
}
#endif
/* More init stuff. */
if (!pk)
{
internal++;
pk = xtrycalloc (1, sizeof *pk);
if (!pk)
{
rc = gpg_error_from_syserror ();
goto leave;
}
}
/* Do a lookup. */
{
struct getkey_ctx_s ctx;
kbnode_t kb = 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;
if (ctrl && ctrl->cached_getkey_kdb)
{
ctx.kr_handle = ctrl->cached_getkey_kdb;
ctrl->cached_getkey_kdb = NULL;
keydb_search_reset (ctx.kr_handle);
}
else
{
ctx.kr_handle = keydb_new (ctrl);
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 (ctrl, &ctx, 0, &kb, &found_key);
if (!rc)
pk_from_block (pk, kb, found_key);
getkey_end (ctrl, &ctx);
if (!rc && r_keyblock)
{
*r_keyblock = kb;
kb = NULL;
}
release_kbnode (kb);
}
if (rc) /* Return a more useful error code. */
rc = gpg_error (GPG_ERR_NO_PUBKEY);
leave:
if (!rc)
cache_public_key (pk);
if (internal)
free_public_key (pk);
return rc;
}
/* Wrapper for get_pubkey_bykid w/o keyblock return feature. */
int
get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid)
{
return get_pubkey_bykid (ctrl, pk, NULL, keyid);
}
/* Same as get_pubkey but if the key was not found the function tries
* to import it from LDAP. FIXME: We should not need this but switch
* to a fingerprint lookup. */
gpg_error_t
get_pubkey_with_ldap_fallback (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid)
{
gpg_error_t err;
err = get_pubkey (ctrl, pk, keyid);
if (!err)
return 0;
if (gpg_err_code (err) != GPG_ERR_NO_PUBKEY)
return err;
/* Note that this code does not handle the case for two readers
* having both openpgp encryption keys. Only one will be tried. */
if (opt.debug)
log_debug ("using LDAP to find a public key\n");
err = keyserver_import_keyid (ctrl, keyid,
opt.keyserver, KEYSERVER_IMPORT_FLAG_LDAP);
if (gpg_err_code (err) == GPG_ERR_NO_DATA
|| gpg_err_code (err) == GPG_ERR_NO_KEYSERVER)
{
/* Dirmngr returns NO DATA is the selected keyserver
* does not have the requested key. It returns NO
* KEYSERVER if no LDAP keyservers are configured. */
err = gpg_error (GPG_ERR_NO_PUBKEY);
}
if (err)
return err;
return get_pubkey (ctrl, pk, keyid);
}
/* 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 (ctrl_t ctrl, 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 (ctrl);
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 only supported FLAGS bit is GETKEY_ALLOW_ADSK.
*
* The self-signed data has already been merged into the public key
* using merge_selfsigs. */
kbnode_t
get_pubkeyblock_ext (ctrl_t ctrl, u32 * keyid, unsigned int flags)
{
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 (ctrl);
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];
- ctx.allow_adsk = !!(flags & GET_PUBKEYBLOCK_FLAG_ADSK);
+ ctx.allow_adsk = !!(flags & GETKEY_ALLOW_ADSK);
rc = lookup (ctrl, &ctx, 0, &keyblock, NULL);
getkey_end (ctrl, &ctx);
return rc ? NULL : keyblock;
}
kbnode_t
get_pubkeyblock (ctrl_t ctrl, u32 * keyid)
{
return get_pubkeyblock_ext (ctrl, keyid, 0);
}
/* 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 (ctrl_t ctrl, 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 (ctrl);
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 (ctrl, &ctx, 1, &keyblock, &found_key);
if (!err)
{
pk_from_block (pk, keyblock, found_key);
}
getkey_end (ctrl, &ctx);
release_kbnode (keyblock);
if (!err)
{
if (!agent_probe_secret_key (/*ctrl*/NULL, pk))
{
release_public_key_parts (pk);
err = gpg_error (GPG_ERR_NO_SECKEY);
}
}
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 *opaque, u32 * keyid, int uid_no)
{
ctrl_t ctrl = opaque;
int unusable = 0;
KBNODE keyblock;
PKT_public_key *pk;
keyblock = get_pubkeyblock (ctrl, 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 && !opt.ignore_expiration))
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->flags.revoked
|| (user_id->flags.expired && !opt.ignore_expiration))
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 the GETKEY_WANT_SECRET bit is set in FLAGS, then only keys with
an available secret key (either locally or via key registered on a
smartcard) are returned.
If the GETKEY_WITH_UNUSABLE bit is set in FLAGS, 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 the GETKEY_ALLOW_ADSK bit is set in FLAGS, ADSK keys are always
+ returned. Without that they are only returned if they have been
+ requested by PK->REQ_USAGE.
+
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, holding the lock.
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 (ctrl_t ctrl, GETKEY_CTX *retctx, strlist_t namelist,
PKT_public_key *pk, unsigned int flags,
kbnode_t *ret_kb, KEYDB_HANDLE *ret_kdbhd)
{
int rc = 0;
int n;
strlist_t r;
strlist_t namelist_expanded = NULL;
GETKEY_CTX ctx;
kbnode_t help_kb = NULL;
kbnode_t 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 (!(flags & GETKEY_WITH_UNUSABLE))
{
ctx->items[0].skipfnc = skip_unusable;
ctx->items[0].skipfncvalue = ctrl;
}
}
else
{
namelist_expanded = expand_group (namelist, 1);
namelist = namelist_expanded;
/* 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);
rc = gpg_err_code (err); /* FIXME: remove gpg_err_code. */
goto leave;
}
if (!(flags & GETKEY_WITH_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_FPR)
{
ctx->items[n].skipfnc = skip_unusable;
ctx->items[n].skipfncvalue = ctrl;
}
}
}
ctx->want_secret = !!(flags & GETKEY_WANT_SECRET);
+ ctx->allow_adsk = !!(flags & GETKEY_ALLOW_ADSK);
ctx->kr_handle = keydb_new (ctrl);
if (!ctx->kr_handle)
{
rc = gpg_error_from_syserror ();
getkey_end (ctrl, ctx);
goto leave;
}
if (!ret_kb)
ret_kb = &help_kb;
if (ret_kdbhd)
keydb_lock (ctx->kr_handle);
+
if (pk)
{
/* It is a bit tricky to allow returning an ADSK key: lookup
* masks the req_usage flags using the standard usage maps and
* only if ctx->allow_adsk is set, sets the RENC flag again. */
ctx->req_usage = pk->req_usage;
if ((pk->req_usage & PUBKEY_USAGE_RENC))
ctx->allow_adsk = 1;
}
rc = lookup (ctrl, ctx, ctx->want_secret, ret_kb, &found_key);
if (!rc && pk)
{
pk_from_block (pk, *ret_kb, found_key);
}
release_kbnode (help_kb);
if (retctx) /* Caller wants the context. */
{
if (ctx->extra_list)
{
for (r=ctx->extra_list; r->next; r = r->next)
;
r->next = namelist_expanded;
}
else
ctx->extra_list = namelist_expanded;
namelist_expanded = NULL;
*retctx = ctx;
}
else
{
if (ret_kdbhd)
{
*ret_kdbhd = ctx->kr_handle;
ctx->kr_handle = NULL;
}
getkey_end (ctrl, ctx);
}
leave:
free_strlist (namelist_expanded);
return rc;
}
/* Find a public key identified by NAME.
*
* If name appears to be a valid RFC822 mailbox (i.e., email address)
* and auto key lookup is enabled (mode != GET_PUBKEY_NO_AKL), 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.
*
* MODE can be one of:
* GET_PUBKEY_NORMAL - The standard mode
* GET_PUBKEY_NO_AKL - 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!
* GET_PUBKEY_NO_LOCAL - Only the auto key locate functionality is
* used and no local search is done.
* GET_PUBKEY_TRY_LDAP - If the key was not found locally try LDAP.
*
* 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.
*
* 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, enum get_pubkey_modes mode,
GETKEY_CTX * retctx, PKT_public_key * pk,
const char *name, KBNODE * ret_keyblock,
KEYDB_HANDLE * ret_kdbhd, int include_unusable)
{
int rc;
strlist_t namelist = NULL;
struct akl *akl;
int is_mbox, is_fpr;
KEYDB_SEARCH_DESC fprbuf;
int nodefault = 0;
int anylocalfirst = 0;
int mechanism_type = AKL_NODEFAULT;
struct akl *used_akl = opt.auto_key_locate;
/* 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);
if (!is_mbox && *name == '<' && name[1] && name[strlen(name)-1]=='>'
&& name[1] != '>'
&& is_valid_mailbox_mem (name+1, strlen (name)-2))
{
/* The mailbox is in the form "<foo@example.org>" which is not
* detected by is_valid_mailbox. Set the flag but keep name as
* it is because the bracketed name is actual the better
* specification for a local search and the other methods
* extract the mail address anyway. */
is_mbox = 1;
}
/* If we are called due to --locate-external-key check whether NAME
* is a fingerprint and then try to lookup that key by configured
* method which support lookup by fingerprint. FPRBUF carries the
* parsed fingerprint iff IS_FPR is true. */
is_fpr = 0;
if (!is_mbox && (mode == GET_PUBKEY_NO_LOCAL || mode == GET_PUBKEY_TRY_LDAP))
{
if (!classify_user_id (name, &fprbuf, 1)
&& fprbuf.mode == KEYDB_SEARCH_MODE_FPR)
is_fpr = 1;
}
/* 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 (mode == GET_PUBKEY_NO_LOCAL)
nodefault = 1; /* Auto-key-locate but ignore "local". */
else if (mode == GET_PUBKEY_NO_AKL)
;
else if (mode == GET_PUBKEY_TRY_LDAP)
{
static struct akl ldap_only_akl = { AKL_LDAP, NULL, NULL };
used_akl = &ldap_only_akl;
}
else
{
/* auto-key-locate is enabled. */
/* nodefault is true if "nodefault" or "local" appear. */
for (akl = used_akl; 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 = used_akl; 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 (mode == GET_PUBKEY_NO_LOCAL)
{
/* Force using the AKL. If IS_MBOX is not set this is the final
* error code. */
rc = GPG_ERR_NO_PUBKEY;
}
else 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 (ctrl, retctx, namelist, pk,
include_unusable? GETKEY_WITH_UNUSABLE:0,
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
&& mode != GET_PUBKEY_NO_AKL
&& (is_mbox || is_fpr))
{
/* 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 = used_akl; akl; akl = akl->next)
{
unsigned char *fpr = NULL;
size_t fpr_len;
int did_akl_local = 0;
int no_fingerprint = 0;
const char *mechanism_string = "?";
mechanism_type = akl->type;
switch (mechanism_type)
{
case AKL_NODEFAULT:
/* This is a dummy mechanism. */
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
break;
case AKL_LOCAL:
if (mode == GET_PUBKEY_NO_LOCAL)
{
/* Note that we get here in is_fpr more, so there is
* no extra check for it required. */
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
}
else
{
mechanism_string = "Local";
did_akl_local = 1;
if (retctx)
{
getkey_end (ctrl, *retctx);
*retctx = NULL;
}
add_to_strlist (&namelist, name);
rc = key_byname (ctrl, anylocalfirst ? retctx : NULL,
namelist, pk,
include_unusable ? GETKEY_WITH_UNUSABLE : 0,
ret_keyblock, ret_kdbhd);
}
break;
case AKL_CERT:
if (is_fpr)
{
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
}
else
{
mechanism_string = "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:
/* This is now obsolete. */
break;
case AKL_DANE:
if (is_fpr)
{
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
break;
}
else
{
mechanism_string = "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:
if (is_fpr)
{
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
}
else
{
mechanism_string = "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:
if (!keyserver_any_configured (ctrl))
{
mechanism_string = "";
rc = GPG_ERR_NO_PUBKEY;
}
else
{
mechanism_string = is_fpr? "ldap/fpr":"ldap/mbox";
glo_ctrl.in_auto_key_retrieve++;
if (is_fpr)
rc = keyserver_import_fpr (ctrl,
fprbuf.u.fpr, fprbuf.fprlen,
opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP);
else
rc = keyserver_import_mbox (ctrl, name, &fpr, &fpr_len,
opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP);
/* Map error codes because Dirmngr returns NO DATA
* if the keyserver does not have the requested key.
* It returns NO KEYSERVER if no LDAP keyservers are
* configured. */
if (gpg_err_code (rc) == GPG_ERR_NO_DATA
|| gpg_err_code (rc) == GPG_ERR_NO_KEYSERVER)
rc = gpg_error (GPG_ERR_NO_PUBKEY);
glo_ctrl.in_auto_key_retrieve--;
}
break;
case AKL_NTDS:
mechanism_string = "NTDS";
glo_ctrl.in_auto_key_retrieve++;
if (is_fpr)
rc = keyserver_import_fpr_ntds (ctrl,
fprbuf.u.fpr, fprbuf.fprlen);
else
rc = keyserver_import_ntds (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_string = "keyserver";
glo_ctrl.in_auto_key_retrieve++;
if (is_fpr)
{
rc = keyserver_import_fpr (ctrl,
fprbuf.u.fpr, fprbuf.fprlen,
opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP);
/* Map error codes because Dirmngr returns NO
* DATA if the keyserver does not have the
* requested key. It returns NO KEYSERVER if no
* LDAP keyservers are configured. */
if (gpg_err_code (rc) == GPG_ERR_NO_DATA
|| gpg_err_code (rc) == GPG_ERR_NO_KEYSERVER)
rc = gpg_error (GPG_ERR_NO_PUBKEY);
}
else
{
rc = keyserver_import_mbox (ctrl, name, &fpr, &fpr_len,
opt.keyserver, 0);
}
glo_ctrl.in_auto_key_retrieve--;
}
else
{
mechanism_string = "Unconfigured keyserver";
rc = GPG_ERR_NO_PUBKEY;
}
break;
case AKL_SPEC:
{
struct keyserver_spec *keyserver;
mechanism_string = akl->spec->uri;
keyserver = keyserver_match (akl->spec);
glo_ctrl.in_auto_key_retrieve++;
if (is_fpr)
{
rc = keyserver_import_fpr (ctrl,
fprbuf.u.fpr, fprbuf.fprlen,
opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP);
if (gpg_err_code (rc) == GPG_ERR_NO_DATA
|| gpg_err_code (rc) == GPG_ERR_NO_KEYSERVER)
rc = gpg_error (GPG_ERR_NO_PUBKEY);
}
else
{
rc = keyserver_import_mbox (ctrl, name,
&fpr, &fpr_len, keyserver, 0);
}
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, 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 || is_fpr))
{
char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1];
if (is_fpr)
{
log_assert (fprbuf.fprlen <= MAX_FINGERPRINT_LEN);
bin2hex (fprbuf.u.fpr, fprbuf.fprlen, fpr_string);
}
else
{
log_assert (fpr_len <= MAX_FINGERPRINT_LEN);
bin2hex (fpr, fpr_len, fpr_string);
}
if (opt.verbose)
log_info ("auto-key-locate found fingerprint %s\n",
fpr_string);
free_strlist (namelist);
namelist = NULL;
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 (ctrl, *retctx);
*retctx = NULL;
}
rc = key_byname (ctrl, anylocalfirst ? retctx : NULL,
namelist, pk,
include_unusable ? GETKEY_WITH_UNUSABLE : 0,
ret_keyblock, ret_kdbhd);
}
if (!rc)
{
/* Key found. */
if (opt.verbose)
log_info (_("automatically retrieved '%s' via %s\n"),
name, mechanism_string);
break;
}
if ((gpg_err_code (rc) != GPG_ERR_NO_PUBKEY
|| opt.verbose || no_fingerprint) && *mechanism_string)
log_info (_("error retrieving '%s' via %s: %s\n"),
name, mechanism_string,
no_fingerprint ? _("No fingerprint") : gpg_strerror (rc));
}
}
if (rc && retctx)
{
getkey_end (ctrl, *retctx);
*retctx = NULL;
}
if (retctx && *retctx)
{
GETKEY_CTX ctx = *retctx;
strlist_t sl;
if (ctx->extra_list)
{
for (sl=ctx->extra_list; sl->next; sl = sl->next)
;
sl->next = namelist;
}
else
ctx->extra_list = namelist;
(*retctx)->found_via_akl = mechanism_type;
}
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->flags.revoked;
}
static int
subkey_is_ok (const PKT_public_key *sub)
{
return ! sub->flags.revoked && sub->flags.valid && ! sub->flags.disabled;
}
/* Return true if KEYBLOCK has only expired encryption subkeys. Note
* that the function returns false if the key has no encryption
* subkeys at all or the subkeys are revoked. */
static int
only_expired_enc_subkeys (kbnode_t keyblock)
{
kbnode_t node;
PKT_public_key *sub;
int any = 0;
for (node = find_next_kbnode (keyblock, PKT_PUBLIC_SUBKEY);
node; node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY))
{
sub = node->pkt->pkt.public_key;
if (!(sub->pubkey_usage & PUBKEY_USAGE_ENC))
continue;
if (!subkey_is_ok (sub))
continue;
any = 1;
if (!sub->has_expired)
return 0;
}
return any? 1 : 0;
}
/* 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;
if ((new->key.pubkey_usage & PUBKEY_USAGE_ENC) == 0)
new->creation_time = 0;
else
new->creation_time = new->key.timestamp;
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;
}
/* When new key has no encryption key, use OLD key. */
if (new->creation_time == 0)
return 1;
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, 0);
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_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 (new->validity != TRUST_EXPIRED && old->validity < new->validity)
return -1; /* Validity of the NEW key is better. */
if (old->validity == TRUST_EXPIRED && new->validity != TRUST_EXPIRED)
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. */
gpg_error_t
get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode,
GETKEY_CTX *retctx, PKT_public_key *pk,
const char *name, KBNODE *ret_keyblock,
int include_unusable)
{
gpg_error_t err;
struct getkey_ctx_s *ctx = NULL;
int is_mbox;
int wkd_tried = 0;
PKT_public_key pk0;
log_assert (ret_keyblock != NULL);
if (retctx)
*retctx = NULL;
memset (&pk0, 0, sizeof pk0);
pk0.req_usage = pk? pk->req_usage : 0;
is_mbox = is_valid_mailbox (name);
if (!is_mbox && *name == '<' && name[1] && name[strlen(name)-1]=='>'
&& name[1] != '>'
&& is_valid_mailbox_mem (name+1, strlen (name)-2))
{
/* The mailbox is in the form "<foo@example.org>" which is not
* detected by is_valid_mailbox. Set the flag but keep name as
* it is because get_pubkey_byname does an is_valid_mailbox_mem
* itself. */
is_mbox = 1;
}
start_over:
if (ctx) /* Clear in case of a start over. */
{
release_kbnode (*ret_keyblock);
*ret_keyblock = NULL;
getkey_end (ctrl, ctx);
ctx = NULL;
}
err = get_pubkey_byname (ctrl, mode,
&ctx, &pk0, name, ret_keyblock,
NULL, include_unusable);
if (err)
{
goto leave;
}
/* If the keyblock was retrieved from the local database and the key
* has expired, do further checks. However, we can do this only if
* the caller requested a keyblock. */
if (is_mbox && ctx && ctx->found_via_akl == AKL_LOCAL)
{
u32 now = make_timestamp ();
int found;
/* If the key has expired and its origin was the WKD then try to
* get a fresh key from the WKD. We also try this if the key
* has any only expired encryption subkeys. In case we checked
* for a fresh copy in the last 3 hours we won't do that again.
* Unfortunately that does not yet work because KEYUPDATE is
* only updated during import iff the key has actually changed
* (see import.c:import_one). */
if (!wkd_tried && pk0.keyorg == KEYORG_WKD
&& (pk0.keyupdate + 3*3600) < now
&& (pk0.has_expired || only_expired_enc_subkeys (*ret_keyblock)))
{
if (opt.verbose)
log_info (_("checking for a fresh copy of an expired key via %s\n"),
"WKD");
wkd_tried = 1;
glo_ctrl.in_auto_key_retrieve++;
found = !keyserver_import_wkd (ctrl, name, 0, NULL, NULL);
glo_ctrl.in_auto_key_retrieve--;
if (found)
{
release_public_key_parts (&pk0);
goto start_over;
}
}
}
if (is_mbox && ctx)
{
/* Rank results and return only the most relevant key for encryption. */
struct pubkey_cmp_cookie best = { 0 };
struct pubkey_cmp_cookie new = { 0 };
kbnode_t new_keyblock;
copy_public_key (&new.key, &pk0);
if (pubkey_cmp (ctrl, name, &best, &new, *ret_keyblock) >= 0)
{
release_public_key_parts (&new.key);
free_user_id (new.uid);
}
else
best = new;
new.uid = NULL;
while (getkey_next (ctrl, 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);
}
new.uid = NULL;
}
getkey_end (ctrl, ctx);
ctx = NULL;
free_user_id (best.uid);
best.uid = NULL;
if (best.valid)
{
ctx = xtrycalloc (1, sizeof **retctx);
if (! ctx)
err = gpg_error_from_syserror ();
else
{
ctx->kr_handle = keydb_new (ctrl);
if (! ctx->kr_handle)
{
err = gpg_error_from_syserror ();
xfree (ctx);
ctx = NULL;
if (retctx)
*retctx = NULL;
}
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];
release_kbnode (*ret_keyblock);
*ret_keyblock = NULL;
err = getkey_next (ctrl, ctx, NULL, ret_keyblock);
}
}
if (pk)
*pk = best.key;
else
release_public_key_parts (&best.key);
release_public_key_parts (&pk0);
}
else
{
if (pk)
*pk = pk0;
else
release_public_key_parts (&pk0);
}
}
else
{
if (pk)
*pk = pk0;
else
release_public_key_parts (&pk0);
}
if (err && ctx)
{
getkey_end (ctrl, ctx);
ctx = NULL;
}
if (retctx && ctx)
{
*retctx = ctx;
ctx = NULL;
}
leave:
getkey_end (ctrl, ctx);
return err;
}
/* 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. If R_KEYBLOCK is not NULL and a key was found the
* keyblock is stored there; otherwiese NULL is stored there.
*
* 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,
kbnode_t *r_keyblock)
{
gpg_error_t err;
kbnode_t keyblock;
kbnode_t found_key;
unsigned int infoflags;
if (r_keyblock)
*r_keyblock = NULL;
err = read_key_from_file_or_buffer (ctrl, fname, NULL, 0, &keyblock);
if (!err)
{
/* Warning: node flag bits 0 and 1 should be preserved by
* merge_selfsigs. FIXME: Check whether this still holds. */
merge_selfsigs (ctrl, keyblock);
found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, 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);
}
if (!err && r_keyblock)
*r_keyblock = keyblock;
else
release_kbnode (keyblock);
return err;
}
/* Return a public key from the buffer (BUFFER, BUFLEN). The key is
* onlyretruned if it matches the keyid given in WANT_KEYID. On
* success the key is stored at the caller provided PKBUF structure.
* The caller must release the content of PK by calling
* release_public_key_parts (or, if PKBUF was malloced, using
* free_public_key). If R_KEYBLOCK is not NULL the full keyblock is
* also stored there. */
gpg_error_t
get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf,
const void *buffer, size_t buflen, u32 *want_keyid,
kbnode_t *r_keyblock)
{
gpg_error_t err;
kbnode_t keyblock;
kbnode_t node;
PKT_public_key *pk;
if (r_keyblock)
*r_keyblock = NULL;
err = read_key_from_file_or_buffer (ctrl, NULL, buffer, buflen, &keyblock);
if (!err)
{
merge_selfsigs (ctrl, keyblock);
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;
keyid_from_pk (pk, NULL);
if (pk->keyid[0] == want_keyid[0]
&& pk->keyid[1] == want_keyid[1])
break;
}
}
if (node)
copy_public_key (pkbuf, pk);
else
err = gpg_error (GPG_ERR_NO_PUBKEY);
}
if (!err && r_keyblock)
*r_keyblock = keyblock;
else
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 xmalloc, 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().
*
* FPR is a byte array whose contents is the fingerprint to use as
* the search term. FPRLEN specifies the length of the
* fingerprint (in bytes). Currently, only 16, 20, and 32-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_byfpr (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
const byte *fpr, size_t fprlen)
{
int rc;
if (r_keyblock)
*r_keyblock = NULL;
if (fprlen == 32 || fprlen == 20 || fprlen == 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;
/* FIXME: We should get the handle from the cache like we do in
* get_pubkey. */
ctx.kr_handle = keydb_new (ctrl);
if (!ctx.kr_handle)
return gpg_error_from_syserror ();
ctx.nitems = 1;
ctx.items[0].mode = KEYDB_SEARCH_MODE_FPR;
memcpy (ctx.items[0].u.fpr, fpr, fprlen);
ctx.items[0].fprlen = fprlen;
if (pk)
ctx.req_usage = pk->req_usage;
rc = lookup (ctrl, &ctx, 0, &kb, &found_key);
if (!rc && pk)
pk_from_block (pk, kb, found_key);
if (!rc && r_keyblock)
{
*r_keyblock = kb;
kb = NULL;
}
release_kbnode (kb);
getkey_end (ctrl, &ctx);
}
else
rc = GPG_ERR_GENERAL; /* Oops */
return rc;
}
/* This function is similar to get_pubkey_byfpr, 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_byfpr, PK may be NULL. In that case, this
* function effectively just checks for the existence of the key. */
gpg_error_t
get_pubkey_byfpr_fast (ctrl_t ctrl, PKT_public_key * pk,
const byte *fpr, size_t fprlen)
{
gpg_error_t err;
KBNODE keyblock;
err = get_keyblock_byfpr_fast (ctrl, &keyblock, NULL, 0, fpr, fprlen, 0);
if (!err)
{
if (pk)
copy_public_key (pk, keyblock->pkt->pkt.public_key);
release_kbnode (keyblock);
}
return err;
}
/* This function is similar to get_pubkey_byfpr_fast but returns a
* keydb handle at R_HD and the keyblock at R_KEYBLOCK. R_KEYBLOCK or
* R_HD may be NULL. If LOCK is set the handle has been opend in
* locked mode and keydb_disable_caching () has been called. On error
* R_KEYBLOCK is set to NULL but R_HD must be released by the caller;
* it may have a value of NULL, though. This allows one to do an
* insert operation on a locked keydb handle. If PRIMARY_ONLY is set
* the function returns a keyblock which has the requested fingerprint
* has primary key. */
gpg_error_t
get_keyblock_byfpr_fast (ctrl_t ctrl,
kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
int primary_only,
const byte *fpr, size_t fprlen, int lock)
{
gpg_error_t err;
KEYDB_HANDLE hd;
kbnode_t keyblock;
byte fprbuf[MAX_FINGERPRINT_LEN];
int i;
byte tmpfpr[MAX_FINGERPRINT_LEN];
size_t tmpfprlen;
if (r_keyblock)
*r_keyblock = NULL;
if (r_hd)
*r_hd = NULL;
for (i = 0; i < MAX_FINGERPRINT_LEN && i < fprlen; i++)
fprbuf[i] = fpr[i];
hd = keydb_new (ctrl);
if (!hd)
return gpg_error_from_syserror ();
if (lock)
{
err = keydb_lock (hd);
if (err)
{
/* If locking did not work, we better don't return a handle
* at all - there was a reason that locking has been
* requested. */
keydb_release (hd);
return err;
}
keydb_disable_caching (hd);
}
/* For all other errors we return the handle. */
if (r_hd)
*r_hd = hd;
again:
err = keydb_search_fpr (hd, fprbuf, fprlen);
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
{
if (!r_hd)
keydb_release (hd);
return gpg_error (GPG_ERR_NO_PUBKEY);
}
err = keydb_get_keyblock (hd, &keyblock);
if (err)
{
log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (err));
if (!r_hd)
keydb_release (hd);
return gpg_error (GPG_ERR_NO_PUBKEY);
}
log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
|| keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY);
if (primary_only)
{
fingerprint_from_pk (keyblock->pkt->pkt.public_key, tmpfpr, &tmpfprlen);
if (fprlen != tmpfprlen || memcmp (fpr, tmpfpr, fprlen))
{
release_kbnode (keyblock);
keyblock = NULL;
goto again;
}
}
/* Not caching key here since it won't have all of the fields
properly set. */
if (r_keyblock)
*r_keyblock = keyblock;
else
release_kbnode (keyblock);
if (!r_hd)
keydb_release (hd);
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_t kb;
kbnode_t node;
int any_revoked, any_expired, any_disabled;
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 (ctrl);
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 (ctrl, kb);
any_revoked = any_expired = any_disabled = 0;
err = gpg_error (GPG_ERR_NO_SECKEY);
node = kb;
do
{
PKT_public_key *pk = node->pkt->pkt.public_key;
/* Check if the key is valid. */
if (pk->flags.revoked)
{
any_revoked = 1;
if (DBG_LOOKUP)
log_debug ("not using %s as default key, %s",
keystr_from_pk (pk), "revoked");
continue;
}
if (pk->has_expired)
{
any_expired = 1;
if (DBG_LOOKUP)
log_debug ("not using %s as default key, %s",
keystr_from_pk (pk), "expired");
continue;
}
if (pk_is_disabled (pk))
{
any_disabled = 1;
if (DBG_LOOKUP)
log_debug ("not using %s as default key, %s",
keystr_from_pk (pk), "disabled");
continue;
}
if (agent_probe_secret_key (ctrl, pk))
{
/* This is a valid key. */
err = 0;
break;
}
}
while ((node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY)));
release_kbnode (kb);
if (err)
{
if (! warned && ! opt.quiet)
{
gpg_err_code_t ec;
/* Try to get a better error than no secret key if we
* only know that the public key is not usable. */
if (any_revoked)
ec = GPG_ERR_CERT_REVOKED;
else if (any_expired)
ec = GPG_ERR_KEY_EXPIRED;
else if (any_disabled)
ec = GPG_ERR_KEY_DISABLED;
else
ec = GPG_ERR_NO_SECKEY;
log_info (_("Warning: not using '%s' as default key: %s\n"),
t->d, gpg_strerror (ec));
print_reported_error (err, ec);
}
}
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;
unsigned int flags = GETKEY_WANT_SECRET | GETKEY_WITH_UNUSABLE;
const char *def_secret_key = parse_def_secret_key (ctrl);
if (def_secret_key)
add_to_strlist (&namelist, def_secret_key);
else
flags &= ~GETKEY_WITH_UNUSABLE;
err = key_byname (ctrl, NULL, namelist, pk, flags, 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.
+ * If GETKEY_WANT_SECRET is set in FLAGS, 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 (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk,
- strlist_t names, int want_secret, kbnode_t *ret_keyblock)
+ strlist_t names, unsigned int flags, kbnode_t *ret_keyblock)
{
return key_byname (ctrl, retctx, names, pk,
- ((want_secret ? GETKEY_WANT_SECRET : 0)
- | GETKEY_WITH_UNUSABLE),
+ (flags | GETKEY_WITH_UNUSABLE),
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;
const char *def_secret_key = NULL;
unsigned int flags = GETKEY_WITH_UNUSABLE;
if (want_secret)
flags |= GETKEY_WANT_SECRET;
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
flags &= ~GETKEY_WITH_UNUSABLE;
err = key_byname (ctrl, retctx, namelist, pk, flags, 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 (ctrl_t ctrl, 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 (ctrl, ctx, ctx->want_secret,
ret_keyblock, pk ? &found_key : NULL);
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 (ctrl_t ctrl, getkey_ctx_t ctx)
{
if (ctx)
{
#ifdef HAVE_W32_SYSTEM
/* FIXME: This creates a big regression for Windows because the
* keyring is only released after the global ctrl is released.
* So if an operation does a getkey and then tries to modify the
* keyring it will fail on Windows with a sharing violation. We
* need to modify all keyring write operations to also take the
* ctrl and close the cached_getkey_kdb handle to make writing
* work. See: GnuPG-bug-id: 3097 */
(void)ctrl;
keydb_release (ctx->kr_handle);
#else /*!HAVE_W32_SYSTEM*/
if (ctrl && !ctrl->cached_getkey_kdb)
ctrl->cached_getkey_kdb = ctx->kr_handle;
else
keydb_release (ctx->kr_handle);
#endif /*!HAVE_W32_SYSTEM*/
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 (ctrl_t ctrl, kbnode_t keyblock)
{
if (!keyblock)
;
else if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY)
merge_selfsigs (ctrl, keyblock);
else
log_debug ("FIXME: merging secret key blocks is not anymore available\n");
}
/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */
unsigned int
parse_key_usage (PKT_signature * sig)
{
int key_usage = 0;
const byte *p;
size_t n;
byte flags;
p = parse_sig_subpkt (sig, 1, 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 & 0x80))
{
key_usage |= PUBKEY_USAGE_GROUP;
flags &= ~0x80;
}
if (flags)
key_usage |= PUBKEY_USAGE_UNKNOWN;
n--;
p++;
if (n)
{
flags = *p;
if ((flags & 0x04))
key_usage |= PUBKEY_USAGE_RENC;
if ((flags & 0x08))
key_usage |= PUBKEY_USAGE_TIME;
}
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:
* - whether 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, *aead, *hash, *zip;
size_t n, nsym, naead, nhash, nzip;
sig->flags.chosen_selfsig = 1;/* We chose this one. */
uid->created = 0; /* Not created == invalid. */
if (IS_UID_REV (sig))
{
uid->flags.revoked = 1;
return; /* Has been revoked. */
}
else
uid->flags.revoked = 0;
uid->expiredate = sig->expiredate;
if (sig->flags.expired)
{
uid->flags.expired = 1;
return; /* Has expired. */
}
else
uid->flags.expired = 0;
uid->created = sig->timestamp; /* This one is okay. */
uid->selfsigversion = sig->version;
/* If we got this far, it's not expired :) */
uid->flags.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, 1, 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->flags.primary = 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PRIMARY_UID, NULL);
if (p && *p)
uid->flags.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, 1, SIGSUBPKT_PREF_SYM, &n);
sym = p;
nsym = p ? n : 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_AEAD, &n);
aead = p;
naead = p ? n : 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_HASH, &n);
hash = p;
nhash = p ? n : 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_COMPR, &n);
zip = p;
nzip = p ? n : 0;
if (uid->prefs)
xfree (uid->prefs);
n = nsym + naead + 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 (; naead; naead--, n++)
{
uid->prefs[n].type = PREFTYPE_AEAD;
uid->prefs[n].value = *aead++;
}
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, 1, SIGSUBPKT_FEATURES, &n);
if (p && n && (p[0] & 0x01))
uid->flags.mdc = 1;
/* See whether we have the AEAD feature. */
uid->flags.aead = 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_FEATURES, &n);
if (p && n && (p[0] & 0x02))
uid->flags.aead = 1;
/* And the keyserver modify flag. */
uid->flags.ks_modify = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_KS_FLAGS, &n);
if (p && n && (p[0] & 0x80))
uid->flags.ks_modify = 0;
}
/* Store the revocation signature into the RINFO struct. */
static void
sig_to_revoke_info (PKT_signature * sig, struct revoke_info *rinfo)
{
int reason_seq = 0;
size_t reason_n;
const byte *reason_p;
rinfo->date = sig->timestamp;
rinfo->algo = sig->pubkey_algo;
rinfo->keyid[0] = sig->keyid[0];
rinfo->keyid[1] = sig->keyid[1];
xfree (rinfo->reason_comment);
rinfo->reason_comment = NULL;
rinfo->reason_comment_len = 0;
rinfo->reason_code = 0;
rinfo->got_reason = 0;
while ((reason_p = enum_sig_subpkt (sig, 1, SIGSUBPKT_REVOC_REASON,
&reason_n, &reason_seq, NULL))
&& !reason_n)
; /* Skip over empty reason packets. */
if (reason_p)
{
rinfo->got_reason = 1;
rinfo->reason_code = *reason_p;
reason_n--; reason_p++;
if (reason_n)
{
rinfo->reason_comment = xmalloc (reason_n);
memcpy (rinfo->reason_comment, reason_p, reason_n);
rinfo->reason_comment_len = reason_n;
}
}
}
/* 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 (ctrl_t ctrl, kbnode_t 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; /* Creation time of the key. */
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. Note that parse_keyblock_image ensures that the
first packet is the public key. */
if (keyblock->pkt->pkttype != PKT_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. We do not support v3 keys anymore but
* we keep the code in case a future key versions introduces a
* hard expire time again. */
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 (ctrl, 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++, pk->numrevkeys++)
{
pk->revkey[pk->numrevkeys].class
= sig->revkey[i].class;
pk->revkey[pk->numrevkeys].algid
= sig->revkey[i].algid;
pk->revkey[pk->numrevkeys].fprlen
= sig->revkey[i].fprlen;
memcpy (pk->revkey[pk->numrevkeys].fpr,
sig->revkey[i].fpr, sig->revkey[i].fprlen);
memset (pk->revkey[pk->numrevkeys].fpr
+ sig->revkey[i].fprlen,
0,
sizeof (sig->revkey[i].fpr)
- sig->revkey[i].fprlen);
}
}
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));
}
/* SIGNODE is the direct key signature packet (sigclass 0x1f) with
* the latest creation time. Extract some information from it. */
if (signode)
{
/* 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, 1, 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 (ctrl, 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. */
/* Fixme: 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. */
/* Apply the data from the most recent self-signed packet to
* the preceding user id packet. */
if (uidnode && signode)
{
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 (ctrl, 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 (ctrl, ultimate_pk, sig->keyid) == 0
&& check_key_signature2 (ctrl,
keyblock, k, ultimate_pk,
NULL, NULL, NULL, NULL) == 0
&& get_ownertrust (ctrl, 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 || (!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)
& PUBKEY_USAGE_BASIC_MASK);
}
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 | PUBKEY_USAGE_GROUP);
}
/* 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.
* This may be a different one than from usage computation 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 || (!uid->created && !uiddate)))
{
key_expire = uid->help_key_expire;
uiddate = uid->created;
}
}
}
}
/* Currently only the not anymore supported v3 keys have a maximum
* expiration date, but future key versions may 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->flags.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->flags.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->flags.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->flags.primary = 1;
continue;
}
else
{
if (cmp_user_ids (k->pkt->pkt.user_id,
uidnode->pkt->pkt.user_id) > 0)
{
uidnode->pkt->pkt.user_id->flags.primary = 0;
uidnode = k;
uidnode->pkt->pkt.user_id->flags.primary = 1;
}
else
{
/* just to be safe: */
k->pkt->pkt.user_id->flags.primary = 0;
}
}
}
}
}
}
/* 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)
{
free_seckey_enc (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 recent valid self-signed packet, the
* following field is set:
*
* flags.chosen_selfsig
*/
static void
merge_selfsigs_subkey (ctrl_t ctrl, kbnode_t keyblock, kbnode_t 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 (ctrl, 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)
& PUBKEY_USAGE_BASIC_MASK);
}
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 | PUBKEY_USAGE_GROUP);
}
subpk->pubkey_usage = key_usage;
p = parse_sig_subpkt (sig, 1, 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, 1, SIGSUBPKT_SIGNATURE,
&n, &seq, NULL)))
if (n > 3
&& ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19)
|| (p[0] == 5 && 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, 0, SIGSUBPKT_SIGNATURE,
&n, &seq, NULL)))
if (n > 3
&& ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19)
|| (p[0] == 5 && 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 (ctrl_t ctrl, kbnode_t keyblock)
{
KBNODE k;
int revoked;
struct revoke_info rinfo = { 0 };
PKT_public_key *main_pk;
prefitem_t *prefs;
unsigned int mdc_feature;
unsigned int aead_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 (ctrl, 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 (ctrl, 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)
{
/* Copy RINFO reason part only the first time
* because we don't want to propagate the reason to
* the subkeys. This assumes that we get the public
* key first. */
pk->flags.revoked = revoked;
memcpy (&pk->revoked, &rinfo, sizeof (rinfo));
if (rinfo.got_reason)
{
rinfo.got_reason = 0;
rinfo.reason_code = 0;
rinfo.reason_comment = NULL; /*(owner is pk->revoked)*/
rinfo.reason_comment_len = 0;
}
}
if (main_pk->has_expired)
{
pk->has_expired = main_pk->has_expired;
if (!pk->expiredate || pk->expiredate > main_pk->expiredate)
pk->expiredate = main_pk->expiredate;
}
}
}
goto leave;
}
/* 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 = aead_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->flags.primary)
{
prefs = k->pkt->pkt.user_id->prefs;
mdc_feature = k->pkt->pkt.user_id->flags.mdc;
aead_feature = k->pkt->pkt.user_id->flags.aead;
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;
pk->flags.aead = aead_feature;
}
}
leave:
xfree (rinfo.reason_comment);
}
/* 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 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 decryption 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,
int want_secret, int allow_adsk, 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;
int diag_exactfound = 0;
int verify_mode = 0;
u32 curtime = make_timestamp ();
if (r_flags)
*r_flags = 0;
/* The verify mode is used to change the behaviour so that we can
* return an expired or revoked key for signature verification. */
verify_mode = ((req_usage & PUBKEY_USAGE_VERIFY)
&& (req_usage & (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)));
#define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT)
req_usage &= USAGE_MASK;
/* In allow ADSK mode make sure both encryption bits are set. */
if (allow_adsk && (req_usage & PUBKEY_USAGE_XENC_MASK))
req_usage |= PUBKEY_USAGE_XENC_MASK;
/* Request the primary if we're certifying another key, and also if
* signing data while --pgp7 is on since pgp 7 do
* not understand signatures made by a signing subkey. PGP 8 does. */
req_prim = ((req_usage & PUBKEY_USAGE_CERT)
|| (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. Use this loop also to sort our keys
* found using an ADSK fingerprint. */
for (k = keyblock; k; k = k->next)
{
if ((k->flag & 1) && (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY))
{
if (want_exact)
{
foundk = k;
pk = k->pkt->pkt.public_key;
pk->flags.exact = 1;
diag_exactfound = 1;
break;
}
else if (!allow_adsk && (k->pkt->pkt.public_key->pubkey_usage
== PUBKEY_USAGE_RENC))
{
if (DBG_LOOKUP)
log_debug ("finish_lookup: found via ADSK - not selected\n");
if (r_flags)
*r_flags |= LOOKUP_NOT_SELECTED;
return NULL; /* Not found. */
}
}
}
/* 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%s)\n",
(ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL),
foundk ? "one" : "all", req_usage, verify_mode? ",verify":"");
if (diag_exactfound && DBG_LOOKUP)
log_debug ("\texact search requested and found\n");
if (!req_usage)
{
latest_key = foundk ? foundk : keyblock;
if (DBG_LOOKUP)
log_debug ("\tno usage requested - accepting key\n");
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;
int last_secret_key_avail = 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 | PUBKEY_USAGE_RENC))
& req_usage))
{
if (DBG_LOOKUP)
log_debug ("\tusage does not match: want=%x have=%x\n",
req_usage, pk->pubkey_usage);
continue;
}
if (!verify_mode
&& opt.flags.disable_pqc_encryption
&& pk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey skipped due to option %s\n",
"--disable-pqc-encryption");
continue;
}
n_subkeys++;
if (!verify_mode && pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has been revoked\n");
n_revoked_or_expired++;
continue;
}
if (!verify_mode && pk->has_expired && !opt.ignore_expiration)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey has expired\n");
n_revoked_or_expired++;
continue;
}
if (!verify_mode && pk->timestamp > curtime && !opt.ignore_valid_from)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey not yet valid\n");
continue;
}
if (!verify_mode
&& opt.flags.require_pqc_encryption
&& (req_usage & PUBKEY_USAGE_XENC_MASK)
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
{
if (DBG_LOOKUP)
log_debug ("\tsubkey is not quantum-resistant\n");
continue;
}
if (!verify_mode && want_secret)
{
int secret_key_avail = agent_probe_secret_key (NULL, pk);
if (!secret_key_avail)
{
if (DBG_LOOKUP)
log_debug ("\tno secret key\n");
continue;
}
if (secret_key_avail < last_secret_key_avail)
{
if (DBG_LOOKUP)
log_debug ("\tskipping secret key with lower avail\n");
continue;
}
if (secret_key_avail > last_secret_key_avail)
{
/* Use this key. */
last_secret_key_avail = secret_key_avail;
latest_date = 0;
}
}
if (DBG_LOOKUP)
log_debug ("\tsubkey might be fine%s\n",
verify_mode? " for verification":"");
/* 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 (!verify_mode && pk->flags.revoked)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has been revoked\n");
}
else if (!verify_mode && pk->has_expired)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key has expired\n");
}
else if (!verify_mode
&& opt.flags.require_pqc_encryption
&& (req_usage & PUBKEY_USAGE_XENC_MASK)
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
{
if (DBG_LOOKUP)
log_debug ("\tprimary key is not quantum-resistant\n");
}
else /* Okay. */
{
if (DBG_LOOKUP)
log_debug ("\tprimary key may be used%s\n",
verify_mode? " for verification":"");
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_put_keyblock (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 (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
kbnode_t *ret_keyblock, kbnode_t *ret_found_key)
{
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)
{
rc = agent_probe_any_secret_key (ctrl, keyblock);
if (gpg_err_code(rc) == GPG_ERR_NO_SECKEY)
goto skip; /* No secret key available. */
if (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO)
goto skip; /* Not implemented algo - skip. */
if (rc)
goto found; /* Unexpected error. */
}
/* Warning: node flag bits 0 and 1 should be preserved by
* merge_selfsigs. */
merge_selfsigs (ctrl, keyblock);
found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact,
want_secret, ctx->allow_adsk,
&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;
}
/* If a default key has been specified, return that key. If a card
* based key is also available as indicated by FPR_CARD not being
* NULL, return that key if suitable. */
gpg_error_t
get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk,
const byte *fpr_card, size_t fpr_len)
{
gpg_error_t err;
strlist_t namelist = NULL;
const char *def_secret_key;
def_secret_key = parse_def_secret_key (ctrl);
if (def_secret_key)
add_to_strlist (&namelist, def_secret_key);
else if (fpr_card)
{
err = get_pubkey_byfpr (ctrl, pk, NULL, fpr_card, fpr_len);
if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
{
if (opt.debug)
log_debug ("using LDAP to find public key for current card\n");
err = keyserver_import_fpr (ctrl, fpr_card, fpr_len,
opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP);
if (!err)
err = get_pubkey_byfpr (ctrl, pk, NULL, fpr_card, fpr_len);
else if (gpg_err_code (err) == GPG_ERR_NO_DATA
|| gpg_err_code (err) == GPG_ERR_NO_KEYSERVER)
{
/* Dirmngr returns NO DATA is the selected keyserver
* does not have the requested key. It returns NO
* KEYSERVER if no LDAP keyservers are configured. */
err = gpg_error (GPG_ERR_NO_PUBKEY);
}
}
/* The key on card can be not suitable for requested usage. */
if (gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY)
fpr_card = NULL; /* Fallthrough as no card. */
else
return err; /* Success or other error. */
}
if (!fpr_card || (def_secret_key && *def_secret_key
&& def_secret_key[strlen (def_secret_key)-1] == '!'))
{
err = key_byname (ctrl, NULL, namelist, pk, GETKEY_WANT_SECRET,
NULL, NULL);
}
else
{ /* Default key is specified and card key is also available. */
kbnode_t k, keyblock = NULL;
err = key_byname (ctrl, NULL, namelist, pk, GETKEY_WANT_SECRET,
&keyblock, NULL);
if (err)
goto leave;
for (k = keyblock; k; k = k->next)
{
PKT_public_key *pk_candidate;
char fpr[MAX_FINGERPRINT_LEN];
if (k->pkt->pkttype != PKT_PUBLIC_KEY
&&k->pkt->pkttype != PKT_PUBLIC_SUBKEY)
continue;
pk_candidate = k->pkt->pkt.public_key;
if (!pk_candidate->flags.valid)
continue;
if (!((pk_candidate->pubkey_usage & USAGE_MASK) & pk->req_usage))
continue;
fingerprint_from_pk (pk_candidate, fpr, NULL);
if (!memcmp (fpr_card, fpr, fpr_len))
{
release_public_key_parts (pk);
copy_public_key (pk, pk_candidate);
break;
}
}
release_kbnode (keyblock);
}
leave:
free_strlist (namelist);
return err;
}
/*********************************************
*********** User ID printing helpers *******
*********************************************/
/* Return a string with a printable representation of the user_id.
* this string must be freed by xfree. If R_NOUID is not NULL it is
* set to true if a user id was not found; otherwise to false. */
static char *
get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode)
{
char *name;
unsigned int namelen;
char *p;
log_assert (mode != 2);
name = cache_get_uid_bykid (keyid, &namelen);
if (!name)
{
/* Get it so that the cache will be filled. */
if (!get_pubkey (ctrl, NULL, keyid))
name = cache_get_uid_bykid (keyid, &namelen);
}
if (name)
{
if (mode)
p = xasprintf ("%08lX%08lX %.*s",
(ulong) keyid[0], (ulong) keyid[1], namelen, name);
else
p = xasprintf ("%s %.*s", keystr (keyid), namelen, name);
xfree (name);
}
else
{
if (mode)
p = xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]);
else
p = xasprintf ("%s [?]", keystr (keyid));
}
return p;
}
char *
get_user_id_string_native (ctrl_t ctrl, u32 * keyid)
{
char *p = get_user_id_string (ctrl, keyid, 0);
char *p2 = utf8_to_native (p, strlen (p), 0);
xfree (p);
return p2;
}
char *
get_long_user_id_string (ctrl_t ctrl, u32 * keyid)
{
return get_user_id_string (ctrl, keyid, 1);
}
/* Please try to use get_user_byfpr instead of this one. */
char *
get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid)
{
char *name;
unsigned int namelen;
if (r_nouid)
*r_nouid = 0;
name = cache_get_uid_bykid (keyid, &namelen);
if (!name)
{
/* Get it so that the cache will be filled. */
if (!get_pubkey (ctrl, NULL, keyid))
name = cache_get_uid_bykid (keyid, &namelen);
}
if (!name)
{
name = xstrdup (user_id_not_found_utf8 ());
namelen = strlen (name);
if (r_nouid)
*r_nouid = 1;
}
if (rn && name)
*rn = namelen;
return name;
}
/* Please try to use get_user_id_byfpr_native instead of this one. */
char *
get_user_id_native (ctrl_t ctrl, u32 *keyid)
{
size_t rn;
char *p = get_user_id (ctrl, keyid, &rn, NULL);
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. */
static char *
get_user_id_byfpr (ctrl_t ctrl, const byte *fpr, size_t fprlen, size_t *rn)
{
char *name;
name = cache_get_uid_byfpr (fpr, fprlen, rn);
if (!name)
{
/* Get it so that the cache will be filled. */
if (!get_pubkey_byfpr (ctrl, NULL, NULL, fpr, fprlen))
name = cache_get_uid_byfpr (fpr, fprlen, rn);
}
if (!name)
{
name = xstrdup (user_id_not_found_utf8 ());
*rn = strlen (name);
}
return name;
}
/* 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 (ctrl_t ctrl, const byte *fpr, size_t fprlen)
{
size_t rn;
char *p = get_user_id_byfpr (ctrl, fpr, fprlen, &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 true if the AKL is empty or has only the local method
* active. */
int
akl_empty_or_only_local (void)
{
struct akl *akl;
int any = 0;
for (akl = opt.auto_key_locate; akl; akl = akl->next)
if (akl->type != AKL_NODEFAULT && akl->type != AKL_LOCAL)
{
any = 1;
break;
}
return !any;
}
/* Returns false on error. */
int
parse_auto_key_locate (const char *options_arg)
{
char *tok;
char *options, *options_buf;
options = options_buf = xstrdup (options_arg);
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;
else if (ascii_strcasecmp (tok, "cert") == 0)
akl->type = AKL_CERT;
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 (ascii_strcasecmp (tok, "ntds") == 0)
akl->type = AKL_NTDS;
else if ((akl->spec = parse_keyserver_uri (tok, 1)))
akl->type = AKL_SPEC;
else
{
free_akl (akl);
xfree (options_buf);
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;
}
}
xfree (options_buf);
return 1;
}
/* The list of key origins. */
static struct {
const char *name;
int origin;
} key_origin_list[] =
{
{ "self", KEYORG_SELF },
{ "file", KEYORG_FILE },
{ "url", KEYORG_URL },
{ "wkd", KEYORG_WKD },
{ "dane", KEYORG_DANE },
{ "ks-pref", KEYORG_KS_PREF },
{ "ks", KEYORG_KS },
{ "unknown", KEYORG_UNKNOWN }
};
/* Parse the argument for --key-origin. Return false on error. */
int
parse_key_origin (char *string)
{
int i;
char *comma;
comma = strchr (string, ',');
if (comma)
*comma = 0;
if (!ascii_strcasecmp (string, "help"))
{
log_info (_("valid values for option '%s':\n"), "--key-origin");
for (i=0; i < DIM (key_origin_list); i++)
log_info (" %s\n", key_origin_list[i].name);
g10_exit (1);
}
for (i=0; i < DIM (key_origin_list); i++)
if (!ascii_strcasecmp (string, key_origin_list[i].name))
{
opt.key_origin = key_origin_list[i].origin;
xfree (opt.key_origin_url);
opt.key_origin_url = NULL;
if (comma && comma[1])
{
opt.key_origin_url = xstrdup (comma+1);
trim_spaces (opt.key_origin_url);
}
return 1;
}
if (comma)
*comma = ',';
return 0;
}
/* Return a string or "?" for the key ORIGIN. */
const char *
key_origin_string (int origin)
{
int i;
for (i=0; i < DIM (key_origin_list); i++)
if (key_origin_list[i].origin == origin)
return key_origin_list[i].name;
return "?";
}
/* 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 (ctrl_t ctrl, 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 (ctrl);
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;
}
/* Return an error if KEYBLOCK has a primary or subkey with the given
* fingerprint (FPR,FPRLEN). */
gpg_error_t
has_key_with_fingerprint (kbnode_t keyblock, const byte *fpr, size_t fprlen)
{
kbnode_t node;
PKT_public_key *pk;
byte pkfpr[MAX_FINGERPRINT_LEN];
size_t pkfprlen;
for (node = keyblock; node; node = node->next)
{
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_KEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
pk = node->pkt->pkt.public_key;
fingerprint_from_pk (pk, pkfpr, &pkfprlen);
if (pkfprlen == fprlen && !memcmp (pkfpr, fpr, fprlen))
return gpg_error (GPG_ERR_DUP_KEY);
}
}
return 0;
}
diff --git a/g10/keydb.h b/g10/keydb.h
index 526620ce4..364e1287c 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -1,623 +1,624 @@
/* keydb.h - Key database
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 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 <https://www.gnu.org/licenses/>.
*/
#ifndef G10_KEYDB_H
#define G10_KEYDB_H
#include "../common/types.h"
#include "../common/util.h"
#include "packet.h"
/* What qualifies as a certification (key-signature in contrast to a
* data signature)? Note that a back signature is special and can be
* made by key and data signatures capable subkeys.) */
#define IS_CERT(s) (IS_KEY_SIG(s) \
|| IS_UID_SIG(s) \
|| IS_SUBKEY_SIG(s) \
|| IS_KEY_REV(s) \
|| IS_UID_REV(s) \
|| IS_SUBKEY_REV(s) \
|| IS_ATTST_SIGS(s) )
#define IS_SIG(s) (!IS_CERT(s))
#define IS_KEY_SIG(s) ((s)->sig_class == SIGCLASS_KEY)
#define IS_UID_SIG(s) (((s)->sig_class & ~3) == SIGCLASS_CERT)
#define IS_ATTST_SIGS(s) ((s)->sig_class == 0x16)
#define IS_SUBKEY_SIG(s) ((s)->sig_class == SIGCLASS_SUBKEY)
#define IS_BACK_SIG(s) ((s)->sig_class == SIGCLASS_BACKSIG)
#define IS_KEY_REV(s) ((s)->sig_class == SIGCLASS_KEYREV)
#define IS_UID_REV(s) ((s)->sig_class == SIGCLASS_CERTREV)
#define IS_SUBKEY_REV(s) ((s)->sig_class == SIGCLASS_SUBREV)
struct getkey_ctx_s;
typedef struct getkey_ctx_s *GETKEY_CTX;
typedef struct getkey_ctx_s *getkey_ctx_t;
/****************
* A Keyblock is all packets which form an entire certificate;
* i.e. the public key, certificate, trust packets, user ids,
* signatures, and subkey.
*
* This structure is also used to bind arbitrary packets together.
*/
struct kbnode_struct
{
kbnode_t next;
PACKET *pkt;
int flag; /* Local use during keyblock processing (not cloned).*/
unsigned int tag; /* Ditto. */
int private_flag;
};
#define is_deleted_kbnode(a) ((a)->private_flag & 1)
#define is_cloned_kbnode(a) ((a)->private_flag & 2)
/*
* A structure to store key identification as well as some stuff
* needed for key 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];
};
/* Bit flags used with build_pk_list. */
enum
{
PK_LIST_ENCRYPT_TO = 1, /* This is an encrypt-to recipient. */
PK_LIST_HIDDEN = 2, /* This is a hidden recipient. */
PK_LIST_CONFIG = 4, /* Specified via config file. */
PK_LIST_FROM_FILE = 8 /* Take key from file with that name. */
};
/* To store private data in the flags the private data must be left
* shifted by this value. */
enum
{
PK_LIST_SHIFT = 4
};
/* Structure to hold a couple of public key certificates. */
typedef struct pk_list *PK_LIST; /* Deprecated. */
typedef struct pk_list *pk_list_t;
struct pk_list
{
PK_LIST next;
PKT_public_key *pk;
int flags; /* See PK_LIST_ constants. */
};
/* Structure to hold a list of secret key certificates. */
typedef struct sk_list *SK_LIST;
struct sk_list
{
SK_LIST next;
PKT_public_key *pk;
int mark; /* not used */
};
/* structure to collect all information which can be used to
* identify a public key */
typedef struct pubkey_find_info *PUBKEY_FIND_INFO;
struct pubkey_find_info {
u32 keyid[2];
unsigned nbits;
byte pubkey_algo;
byte fingerprint[MAX_FINGERPRINT_LEN];
char userid[1];
};
/* Helper type for preference functions. */
struct pref_hint
{
int digest_length; /* We want at least this digest length. */
int exact; /* We need to use exactly this length. */
};
/* Constants to describe from where a key was fetched or updated. */
enum
{
KEYORG_UNKNOWN = 0,
KEYORG_KS = 1, /* Public keyserver. */
KEYORG_KS_PREF = 2, /* Preferred keysrver. */
KEYORG_DANE = 3, /* OpenPGP DANE. */
KEYORG_WKD = 4, /* Web Key Directory. */
KEYORG_URL = 5, /* Trusted URL. */
KEYORG_FILE = 6, /* Trusted file. */
KEYORG_SELF = 7 /* We generated it. */
};
/*
* 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;
}
/*-- call-keyboxd.c --*/
/* Release all open contexts to the keyboxd. */
void gpg_keyboxd_deinit_session_data (ctrl_t ctrl);
/* Create a new database handle. Returns NULL on error, sets ERRNO,
* and prints an error diagnostic. */
KEYDB_HANDLE keydb_new (ctrl_t ctrl);
/* Release a keydb handle. */
void keydb_release (KEYDB_HANDLE hd);
/* Take a lock if we are not using the keyboxd. */
gpg_error_t keydb_lock (KEYDB_HANDLE hd);
/* Return the keyblock last found by keydb_search. */
gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb);
/* Update the keyblock KB. */
gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb);
/* Insert a keyblock into one of the storage system. */
gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
/* Delete the currently selected keyblock. */
gpg_error_t keydb_delete_keyblock (KEYDB_HANDLE hd);
/* Clears the current search result and resets the handle's position. */
gpg_error_t keydb_search_reset (KEYDB_HANDLE hd);
/* Search the database for keys matching the search description. */
gpg_error_t keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
size_t ndesc, size_t *descindex);
/*-- keydb.c --*/
#define KEYDB_RESOURCE_FLAG_PRIMARY 2 /* The primary resource. */
#define KEYDB_RESOURCE_FLAG_DEFAULT 4 /* The default one. */
#define KEYDB_RESOURCE_FLAG_READONLY 8 /* Open in read only mode. */
#define KEYDB_RESOURCE_FLAG_GPGVDEF 16 /* Default file for gpgv. */
/* Format a search term for debugging output. The caller must free
the result. */
char *keydb_search_desc_dump (struct keydb_search_desc *desc);
/* Register a resource (keyring or keybox). */
gpg_error_t keydb_add_resource (const char *url, unsigned int flags);
/* Dump some statistics to the log. */
void keydb_dump_stats (void);
/* Set a flag on the handle to suppress use of cached results. This
is required for updating a keyring and for key listings. Fixme:
Using a new parameter for keydb_new might be a better solution. */
void keydb_disable_caching (KEYDB_HANDLE hd);
/* Save the last found state and invalidate the current selection. */
void keydb_push_found_state (KEYDB_HANDLE hd);
/* Restore the previous save state. */
void keydb_pop_found_state (KEYDB_HANDLE hd);
/* Return the file name of the resource. */
const char *keydb_get_resource_name (KEYDB_HANDLE hd);
/* Find the first writable resource. */
gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd);
/* Rebuild the on-disk caches of all key resources. */
void keydb_rebuild_caches (ctrl_t ctrl, int noisy);
/* Return the number of skipped blocks (because they were to large to
read from a keybox) since the last search reset. */
unsigned long keydb_get_skipped_counter (KEYDB_HANDLE hd);
/* Return the first non-legacy key in the database. */
gpg_error_t keydb_search_first (KEYDB_HANDLE hd);
/* Return the next key (not the next matching key!). */
gpg_error_t keydb_search_next (KEYDB_HANDLE hd);
/* This is a convenience function for searching for keys with a long
key id. */
gpg_error_t keydb_search_kid (KEYDB_HANDLE hd, u32 *kid);
/* This is a convenience function for searching for keys by
* fingerprint. */
gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr, size_t fprlen);
/*-- pkclist.c --*/
void show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode );
gpg_error_t check_signatures_trust (ctrl_t ctrl, kbnode_t keyblock,
PKT_public_key *pk, PKT_signature *sig);
void release_pk_list (PK_LIST pk_list);
int expand_id (const char *id, strlist_t *into, unsigned int flags);
strlist_t expand_group (strlist_t input, int prepend_input);
int build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list);
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 algo_available( preftype_t preftype, int algo,
const struct pref_hint *hint );
int select_algo_from_prefs( PK_LIST pk_list, int preftype,
int request, const struct pref_hint *hint);
int select_mdc_from_pklist (PK_LIST pk_list);
aead_algo_t select_aead_from_pklist (pk_list_t pk_list);
void warn_missing_aead_from_pklist (PK_LIST pk_list);
void warn_missing_aes_from_pklist (PK_LIST pk_list);
/*-- skclist.c --*/
int random_is_faked (void);
void release_sk_list( SK_LIST sk_list );
gpg_error_t build_sk_list (ctrl_t ctrl, strlist_t locusr,
SK_LIST *ret_sk_list, unsigned use);
/*-- passphrase.h --*/
/* Flags for passphrase_to_dek */
#define GETPASSWORD_FLAG_SYMDECRYPT 1
int have_static_passphrase(void);
const char *get_static_passphrase (void);
void set_passphrase_from_string(const char *pass);
void read_passphrase_from_fd( int fd );
void passphrase_clear_cache (const char *cacheid);
DEK *passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
int create, int nocache,
const char *tryagain_text, unsigned int flags,
int *canceled);
void set_next_passphrase( const char *s );
char *get_last_passphrase(void);
void next_to_last_passphrase(void);
void emit_status_need_passphrase (ctrl_t ctrl, u32 *keyid,
u32 *mainkeyid, int pubkey_algo);
#define FORMAT_KEYDESC_NORMAL 0
#define FORMAT_KEYDESC_IMPORT 1
#define FORMAT_KEYDESC_EXPORT 2
#define FORMAT_KEYDESC_DELKEY 3
#define FORMAT_KEYDESC_KEYGRIP 4
char *gpg_format_keydesc (ctrl_t ctrl,
PKT_public_key *pk, int mode, int escaped);
/*-- getkey.c --*/
/* Cache a copy of a public key in the public key cache. */
void cache_public_key( PKT_public_key *pk );
/* Disable and drop the public key cache. */
void getkey_disable_caches(void);
/* Return the public key used for signature SIG and store it at PK. */
gpg_error_t get_pubkey_for_sig (ctrl_t ctrl,
PKT_public_key *pk, PKT_signature *sig,
PKT_public_key *forced_pk,
kbnode_t *r_keyblock);
/* Return the public key with the key id KEYID and store it at PK.
* Optionally return the entire keyblock. */
gpg_error_t get_pubkey_bykid (ctrl_t ctrl, PKT_public_key *pk,
kbnode_t *r_keyblock, u32 *keyid);
/* Same as get_pubkey_bykid but w/o r_keyblock. */
int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid);
/* Same as get_pubkey but with auto LDAP fetch. */
gpg_error_t get_pubkey_with_ldap_fallback (ctrl_t ctrl,
PKT_public_key *pk, u32 * keyid);
/* 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. */
int get_pubkey_fast (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid);
/* Return the entire keyblock used to create SIG. This is a
* specialized version of get_pubkeyblock. */
kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig);
/* Return the key block for the key with KEYID. */
-#define GET_PUBKEYBLOCK_FLAG_ADSK 1 /* Allow returning ADSK key. */
kbnode_t get_pubkeyblock_ext (ctrl_t ctrl, u32 *keyid, unsigned int flags);
kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 *keyid);
/* A list used by get_pubkeys to gather all of the matches. */
struct pubkey_s
{
struct pubkey_s *next;
/* The key to use (either the public key or the subkey). */
PKT_public_key *pk;
kbnode_t keyblock;
};
typedef struct pubkey_s *pubkey_t;
/* Free a list of public keys. */
void pubkeys_free (pubkey_t keys);
/* Mode flags for get_pubkey_byname. */
enum get_pubkey_modes
{
GET_PUBKEY_NORMAL = 0,
GET_PUBKEY_NO_AKL = 1,
GET_PUBKEY_NO_LOCAL = 2,
GET_PUBKEY_TRY_LDAP = 3
};
/* Other flags for functions in getkey.c */
#define GETKEY_WANT_SECRET 1 /* Only return keys having a secret key. */
#define GETKEY_WITH_UNUSABLE 2 /* Include unusable keys. */
+#define GETKEY_ALLOW_ADSK 4 /* Always return ADSK keys. */
+
/* Find a public key identified by NAME. */
int get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode,
GETKEY_CTX *retctx, PKT_public_key *pk,
const char *name,
KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd,
int include_unusable);
/* Likewise, but only return the best match if NAME resembles a mail
* address. */
gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode,
GETKEY_CTX *retctx, PKT_public_key *pk,
const char *name, KBNODE *ret_keyblock,
int include_unusable);
/* Get a public key directly from file FNAME. */
gpg_error_t get_pubkey_fromfile (ctrl_t ctrl,
PKT_public_key *pk, const char *fname,
kbnode_t *r_keyblock);
/* Get a public key from a buffer. */
gpg_error_t get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf,
const void *buffer, size_t buflen,
u32 *want_keyid, kbnode_t *r_keyblock);
/* Return the public key with the key id KEYID iff the secret key is
* available and store it at PK. */
gpg_error_t get_seckey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid);
/* Lookup a key with the specified fingerprint. */
int get_pubkey_byfpr (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
const byte *fpr, size_t fprlen);
/* This function is similar to get_pubkey_byfpr, but it doesn't
merge the self-signed data into the public key and subkeys or into
the user ids. */
gpg_error_t get_pubkey_byfpr_fast (ctrl_t ctrl, PKT_public_key *pk,
const byte *fpr, size_t fprlen);
/* 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. */
gpg_error_t get_keyblock_byfpr_fast (ctrl_t ctrl,
kbnode_t *r_keyblock,
KEYDB_HANDLE *r_hd,
int primary_only,
const byte *fpr, size_t fprlen,
int lock);
/* Returns true if a secret key is available for the public key with
key id KEYID. */
int have_secret_key_with_kid (ctrl_t ctrl, u32 *keyid);
/* Parse the --default-key parameter. Returns the last key (in terms
of when the option is given) that is available. */
const char *parse_def_secret_key (ctrl_t ctrl);
/* Look up a secret key. */
gpg_error_t get_seckey_default (ctrl_t ctrl, PKT_public_key *pk);
gpg_error_t get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk,
const byte *fpr, size_t fpr_len);
/* Search for keys matching some criteria. */
gpg_error_t getkey_bynames (ctrl_t ctrl,
getkey_ctx_t *retctx, PKT_public_key *pk,
- strlist_t names, int want_secret,
+ strlist_t names, unsigned int flags,
kbnode_t *ret_keyblock);
/* Search for one key matching some criteria. */
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);
/* Return the next search result. */
gpg_error_t getkey_next (ctrl_t ctrl, getkey_ctx_t ctx,
PKT_public_key *pk, kbnode_t *ret_keyblock);
/* Release any resources used by a key listing context. */
void getkey_end (ctrl_t ctrl, getkey_ctx_t ctx);
/* Return the database handle used by this context. The context still
owns the handle. */
KEYDB_HANDLE get_ctx_handle(GETKEY_CTX ctx);
/* Enumerate some secret keys. */
gpg_error_t enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *pk);
/* Set the mainkey_id fields for all keys in KEYBLOCK. */
void setup_main_keyids (kbnode_t keyblock);
/* This function merges information from the self-signed data into the
data structures. */
void merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock);
/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */
unsigned int parse_key_usage (PKT_signature *sig);
char *get_user_id_string_native (ctrl_t ctrl, u32 *keyid);
char *get_long_user_id_string (ctrl_t ctrl, u32 *keyid);
char *get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid);
char *get_user_id_native (ctrl_t ctrl, u32 *keyid);
char *get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr, size_t fprlen);
void release_akl(void);
int akl_empty_or_only_local (void);
int parse_auto_key_locate(const char *options);
int parse_key_origin (char *string);
const char *key_origin_string (int origin);
/* Return an error if KEYBLOCK has a primary or subkey with the fpr. */
gpg_error_t has_key_with_fingerprint (kbnode_t keyblock,
const byte *fpr, size_t fprlen);
/*-- keyid.c --*/
int pubkey_letter( int algo );
char *pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize);
int compare_pubkey_string (const char *astr, const char *bstr);
#define PUBKEY_STRING_SIZE 32
u32 v3_keyid (gcry_mpi_t a, u32 *ki);
void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk );
char *format_keyid (u32 *keyid, int format, char *buffer, int len);
/* Return PK's keyid. The memory is owned by PK. */
u32 *pk_keyid (PKT_public_key *pk);
/* Return the keyid of the primary key associated with PK. The memory
is owned by PK. */
u32 *pk_main_keyid (PKT_public_key *pk);
/* Order A and B. If A < B then return -1, if A == B then return 0,
and if A > B then return 1. */
static int GPGRT_ATTR_UNUSED
keyid_cmp (const u32 *a, const u32 *b)
{
if (a[0] < b[0])
return -1;
if (a[0] > b[0])
return 1;
if (a[1] < b[1])
return -1;
if (a[1] > b[1])
return 1;
return 0;
}
/* Return true if both keyids are equal. */
static int GPGRT_ATTR_UNUSED
keyid_eq (const u32 *a, const u32 *b)
{
return a[0] == b[0] && a[1] == b[1];
}
/* Return whether PK is a primary key. */
static int GPGRT_ATTR_UNUSED
pk_is_primary (PKT_public_key *pk)
{
return keyid_eq (pk_keyid (pk), pk_main_keyid (pk));
}
/* Copy the keyid in SRC to DEST and return DEST. */
u32 *keyid_copy (u32 *dest, const u32 *src);
size_t keystrlen(void);
const char *keystr(u32 *keyid);
const char *keystr_with_sub (u32 *main_kid, u32 *sub_kid);
const char *keystr_from_pk(PKT_public_key *pk);
const char *keystr_from_pk_with_sub (PKT_public_key *main_pk,
PKT_public_key *sub_pk);
/* Return PK's key id as a string using the default format. PK owns
the storage. */
const char *pk_keyid_str (PKT_public_key *pk);
const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc);
u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid );
u32 keyid_from_sig (PKT_signature *sig, u32 *keyid );
u32 keyid_from_fingerprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len,
u32 *keyid);
byte *namehash_from_uid(PKT_user_id *uid);
unsigned nbits_from_pk( PKT_public_key *pk );
/* Convert an UTC TIMESTAMP into an UTC yyyy-mm-dd string. Return
* that string. The caller should pass a buffer with at least a size
* of MK_DATESTR_SIZE. */
char *mk_datestr (char *buffer, size_t bufsize, u32 timestamp);
#define MK_DATESTR_SIZE 11
const char *dateonlystr_from_pk (PKT_public_key *pk);
const char *datestr_from_pk( PKT_public_key *pk );
const char *dateonlystr_from_sig( PKT_signature *sig );
const char *datestr_from_sig( PKT_signature *sig );
const char *expirestr_from_pk( PKT_public_key *pk );
const char *expirestr_from_sig( PKT_signature *sig );
const char *revokestr_from_pk( PKT_public_key *pk );
const char *usagestr_from_pk (PKT_public_key *pk, int fill);
const char *colon_strtime (u32 t);
const char *colon_datestr_from_pk (PKT_public_key *pk);
const char *colon_datestr_from_sig (PKT_signature *sig);
const char *colon_expirestr_from_sig (PKT_signature *sig);
byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len );
byte *v5_fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len);
void fpr20_from_pk (PKT_public_key *pk, byte array[20]);
void fpr20_from_fpr (const byte *fpr, unsigned int fprlen, byte array[20]);
char *hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen);
char *v5hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen);
char *format_hexfingerprint (const char *fingerprint,
char *buffer, size_t buflen);
gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array,
int get_second);
gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip);
char *ecdh_param_str_from_pk (PKT_public_key *pk);
/*-- kbnode.c --*/
KBNODE new_kbnode( PACKET *pkt );
kbnode_t new_kbnode2 (kbnode_t list, PACKET *pkt);
KBNODE clone_kbnode( KBNODE node );
void release_kbnode( KBNODE n );
void delete_kbnode( KBNODE node );
void add_kbnode( KBNODE root, KBNODE node );
void insert_kbnode( KBNODE root, KBNODE node, int pkttype );
void move_kbnode( KBNODE *root, KBNODE node, KBNODE where );
void remove_kbnode( KBNODE *root, KBNODE node );
KBNODE find_prev_kbnode( KBNODE root, KBNODE node, int pkttype );
KBNODE find_next_kbnode( KBNODE node, int pkttype );
KBNODE find_kbnode( KBNODE node, int pkttype );
KBNODE walk_kbnode( KBNODE root, KBNODE *context, int all );
void clear_kbnode_flags( KBNODE n );
int commit_kbnode( KBNODE *root );
void dump_kbnode( KBNODE node );
#endif /*G10_KEYDB_H*/
diff --git a/g10/keylist.c b/g10/keylist.c
index e45471e87..aabffe9bb 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -1,2934 +1,2937 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_DOSISH_SYSTEM
# include <fcntl.h> /* for setmode() */
#endif
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "keydb.h"
#include "photoid.h"
#include "../common/util.h"
#include "../common/ttyio.h"
#include "trustdb.h"
#include "main.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "call-agent.h"
#include "../common/mbox-util.h"
#include "../common/zb32.h"
#include "tofu.h"
#include "../common/init.h"
#include "../common/recsel.h"
#include "../common/compliance.h"
#include "../common/pkscreening.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, int no_local);
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. */
};
/* An object and a global instance to store selectors created from
* --list-filter select=EXPR.
*/
struct list_filter_s
{
recsel_expr_t selkey;
};
struct list_filter_s list_filter;
/* The stream used to write attribute packets to. */
static estream_t attrib_fp;
static gpg_error_t list_keyblock (ctrl_t ctrl,
kbnode_t keyblock, int secret, int has_secret,
int fpr, struct keylist_context *listctx);
/* Release resources from a keylist context. */
static void
keylist_context_release (struct keylist_context *listctx)
{
(void)listctx; /* Nothing to release. */
}
static void
release_list_filter (struct list_filter_s *filt)
{
recsel_release (filt->selkey);
filt->selkey = NULL;
}
static void
cleanup_keylist_globals (void)
{
release_list_filter (&list_filter);
}
/* Parse and set an list filter from string. STRING has the format
* "NAME=EXPR" with NAME being the name of the filter. Spaces before
* and after NAME are not allowed. If this function is all called
* several times all expressions for the same NAME are concatenated.
* Supported filter names are:
*
* - select :: If the expression evaluates to true for a certain key
* this key will be listed. The expression may use any
* variable defined for the export and import filters.
*
*/
gpg_error_t
parse_and_set_list_filter (const char *string)
{
gpg_error_t err;
/* Auto register the cleanup function. */
register_mem_cleanup_func (cleanup_keylist_globals);
if (!strncmp (string, "select=", 7))
err = recsel_parse_expr (&list_filter.selkey, string+7);
else
err = gpg_error (GPG_ERR_INV_NAME);
if (!err && DBG_RECSEL)
recsel_dump (list_filter.selkey);
return err;
}
/* 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; if
* in addition NO_LOCAL is set the locate does not look into the local
* keyring. */
void
public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local)
{
#ifndef NO_TRUST_MODELS
if (opt.with_colons)
{
byte trust_model, marginals, completes, cert_depth, min_cert_level;
ulong created, nextcheck;
read_trust_options (ctrl, &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, no_local);
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);
}
/* Helper for print_key_info and print_key_info_log. */
static char *
format_key_info (ctrl_t ctrl, PKT_public_key *pk, int secret)
{
u32 keyid[2];
char *p;
char pkstrbuf[PUBKEY_STRING_SIZE];
char *result;
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 (ctrl, keyid);
result = xtryasprintf ("%s %s/%s %s %s",
secret? (pk->flags.primary? "sec":"ssb")
/* */ : (pk->flags.primary? "pub":"sub"),
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr (keyid), datestr_from_pk (pk), p);
xfree (p);
return result;
}
/* Print basic information about a public or secret key. With FP
* passed as NULL, the tty output interface is used, otherwise output
* is directed to the given stream. INDENT gives the requested
* indentation; if that is a negative value indentation is suppressed
* for the first line. SECRET tells that the PK has a secret part.
* FIXME: This is similar in use to print_key_line and thus both
* functions should eventually be united.
*/
void
print_key_info (ctrl_t ctrl, estream_t fp,
int indent, PKT_public_key *pk, int secret)
{
int indentabs = indent >= 0? indent : -indent;
char *info;
/* Note: Negative values for INDENT are not yet needed. */
info = format_key_info (ctrl, pk, secret);
if (!fp && indent >= 0)
tty_printf ("\n"); /* (Backward compatibility to old code) */
tty_fprintf (fp, "%*s%s\n", indentabs, "",
info? info : "[Ooops - out of core]");
xfree (info);
}
/* Same as print_key_info put print using the log functions at
* LOGLEVEL. */
void
print_key_info_log (ctrl_t ctrl, int loglevel,
int indent, PKT_public_key *pk, int secret)
{
int indentabs = indent >= 0? indent : -indent;
char *info;
info = format_key_info (ctrl, pk, secret);
log_log (loglevel, "%*s%s\n", indentabs, "",
info? info : "[Ooops - out of core]");
xfree (info);
}
/* 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*/
/* Print the preferences line. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info
* 2 - print colon delimited pfc record to stdout.
*/
void
show_preferences (PKT_user_id *uid, int indent, int mode, int verbose)
{
estream_t fp;
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 (mode < 0 )
fp = NULL;
else if (mode == 1)
fp = log_get_stream ();
else /* Mode 0 or 2 */
fp = es_stdout;
if (mode == 2) /* Print preference record. */
{
static char ptypes[] = { PREFTYPE_SYM, PREFTYPE_HASH,
PREFTYPE_ZIP, PREFTYPE_AEAD };
int t, any;
es_fputs ("pfc::", fp); /* Second field is RFU */
for (t=0; t < DIM (ptypes); t++)
{
any = 0;
for (i = 0; prefs[i].type; i++)
if (prefs[i].type == ptypes[t])
{
es_fprintf (fp, "%s%d", any? ",":"", prefs[i].value);
any = 1;
}
es_putc (':', fp);
}
es_putc (':', fp); /* Two more reserved fields. */
es_putc (':', fp);
any = 0;
if (uid->flags.mdc)
{ es_fprintf (fp, "mdc"); any = 1; }
if (uid->flags.aead)
{ es_fprintf (fp, "%saead", any?",":""); any = 1; };
if (!uid->flags.ks_modify)
{ es_fprintf (fp, "%sno-ks-modify", any?",":""); }
es_putc (':', fp); /* One final colon for easier reading. */
es_putc ('\n', fp);
}
else if (verbose)
{
int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0;
tty_fprintf (fp, "%*s %s", indent, "", _("Cipher: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_SYM)
{
if (any)
tty_fprintf (fp, ", ");
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_fprintf (fp, "%s", openpgp_cipher_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == CIPHER_ALGO_3DES)
des_seen = 1;
}
}
if (!des_seen)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES));
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("AEAD: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_AEAD)
{
if (any)
tty_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (!openpgp_aead_test_algo (prefs[i].value)
&& prefs[i].value < 100)
tty_fprintf (fp, "%s", openpgp_aead_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
}
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("Digest: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_HASH)
{
if (any)
tty_fprintf (fp, ", ");
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_fprintf (fp, "%s", gcry_md_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == DIGEST_ALGO_SHA1)
sha1_seen = 1;
}
}
if (!sha1_seen)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "%s", gcry_md_algo_name (DIGEST_ALGO_SHA1));
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("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_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (s && prefs[i].value < 100)
tty_fprintf (fp, "%s", s);
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == COMPRESS_ALGO_NONE)
uncomp_seen = 1;
}
}
if (!uncomp_seen)
{
if (any)
tty_fprintf (fp, ", ");
else
{
tty_fprintf (fp, "%s",
compress_algo_to_string (COMPRESS_ALGO_ZIP));
tty_fprintf (fp, ", ");
}
tty_fprintf (fp, "%s", compress_algo_to_string (COMPRESS_ALGO_NONE));
}
if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify)
{
tty_fprintf (fp, "\n%*s %s", indent, "", _("Features: "));
any = 0;
if (uid->flags.mdc)
{
tty_fprintf (fp, "MDC");
any = 1;
}
if (uid->flags.aead)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "AEAD");
}
if (!uid->flags.ks_modify)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, _("Keyserver no-modify"));
}
}
tty_fprintf (fp, "\n");
}
else
{
tty_fprintf (fp, "%*s", indent, "");
for (i = 0; prefs[i].type; i++)
{
tty_fprintf (fp, " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' :
prefs[i].type == PREFTYPE_AEAD ? 'A' :
prefs[i].type == PREFTYPE_HASH ? 'H' :
prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?',
prefs[i].value);
}
if (uid->flags.mdc)
tty_fprintf (fp, " [mdc]");
if (uid->flags.aead)
tty_fprintf (fp, " [aead]");
if (!uid->flags.ks_modify)
tty_fprintf (fp, " [no-ks-modify]");
tty_fprintf (fp, "\n");
}
}
/* 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:
* -1 - print to the TTY
* 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 < 0? NULL : mode ? log_get_stream () : es_stdout;
while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_POLICY, &len, &seq, &crit)))
{
if (mode != 2)
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (crit)
str = _("Critical signature policy: ");
else
str = _("Signature policy: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
tty_print_utf8_string2 (fp, p, len, 0);
tty_fprintf (fp, "\n");
}
if (mode > 0)
write_status_buffer (STATUS_POLICY_URL, p, len, 0);
}
}
/* Print a keyserver URL. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*/
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 < 0? NULL : mode ? log_get_stream () : es_stdout;
while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &len, &seq, &crit)))
{
if (mode != 2)
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (crit)
str = _("Critical preferred keyserver: ");
else
str = _("Preferred keyserver: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
tty_print_utf8_string2 (fp, p, len, 0);
tty_fprintf (fp, "\n");
}
if (mode > 0)
status_one_subpacket (SIGSUBPKT_PREF_KS, len,
(crit ? 0x02 : 0) | 0x01, p);
}
}
/* Print notation data. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*
* Defined bits in WHICH:
* 1 - standard notations
* 2 - user notations
* 4 - print notations normally hidden
*/
void
show_notation (PKT_signature * sig, int indent, int mode, int which)
{
estream_t fp = mode < 0? NULL : 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 (!(which & 4) && !strcmp (nd->name, "manu"))
continue;
if (mode != 2)
{
int has_at = !!strchr (nd->name, '@');
if ((which & 1 && !has_at) || (which & 2 && has_at))
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (nd->flags.critical)
str = _("Critical signature notation: ");
else
str = _("Signature notation: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
/* This is all UTF8 */
tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0);
tty_fprintf (fp, "=");
tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0);
/* (We need to use log_printf so that the next call to a
log function does not insert an extra LF.) */
if (mode > 0)
log_printf ("\n");
else
tty_fprintf (fp, "\n");
}
}
if (mode > 0)
{
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");
if (!nd->flags.human && nd->bdat && nd->blen)
write_status_buffer (STATUS_NOTATION_DATA,
nd->bdat, nd->blen, 250);
else
write_status_buffer (STATUS_NOTATION_DATA,
nd->value, strlen (nd->value), 50);
}
}
free_notation (notations);
}
/* Output all the notation data in SIG matching a name given by
* --print-notation to stdout. */
void
print_matching_notations (PKT_signature *sig)
{
notation_t nd, notations;
strlist_t sl;
const char *s;
if (!opt.print_notations)
return;
notations = sig_to_notation (sig);
for (nd = notations; nd; nd = nd->next)
{
for (sl=opt.print_notations; sl; sl = sl->next)
if (!strcmp (sl->d, nd->name))
break;
if (!sl || !*nd->value)
continue;
es_fprintf (es_stdout, "%s: ", nd->name);
for (s = nd->value; *s; s++)
{
if (*s == '\n')
es_fprintf (es_stdout, "\n%*s", (int)strlen (nd->name)+2, "");
else if (*s >= ' ' || *s != '\t')
es_putc (*s, es_stdout);
}
es_putc ('\n', es_stdout);
}
free_notation (notations);
}
static void
print_signature_stats (struct keylist_context *s)
{
if (!s->check_sigs)
return; /* Signature checking was not requested. */
/* Better flush stdout so that the stats are always printed after
* the output. */
es_fflush (es_stdout);
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;
gpg_error_t listerr = 0;
memset (&listctx, 0, sizeof (listctx));
if (opt.check_sigs)
listctx.check_sigs = 1;
hd = keydb_new (ctrl);
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
{
if (secret)
glo_ctrl.silence_parse_warnings++;
rc = keydb_get_keyblock (hd, &keyblock);
if (secret)
glo_ctrl.silence_parse_warnings--;
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 (ctrl, keyblock);
else
any_secret = 0;
if (secret && !any_secret)
; /* Secret key listing requested but this isn't one. */
else
{
if (!opt.with_colons && !(opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
{
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 (ctrl, keyblock);
listerr = list_keyblock (ctrl, keyblock, secret, any_secret,
opt.fingerprint, &listctx);
}
release_kbnode (keyblock);
keyblock = NULL;
}
while (!listerr && !(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;
int any_secret;
const char *resname;
const char *keyring_str = _("Keyring");
int i;
struct keylist_context listctx;
gpg_error_t listerr = 0;
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 whether 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 (ctrl, &ctx, NULL, names, secret, &keyblock);
+ rc = getkey_bynames (ctrl, &ctx, NULL, names,
+ (GETKEY_ALLOW_ADSK
+ | (secret ? GETKEY_WANT_SECRET : 0)),
+ &keyblock);
if (rc)
{
log_error ("error reading key: %s\n", gpg_strerror (rc));
getkey_end (ctrl, ctx);
write_status_error ("keylist.getkey", rc);
return;
}
do
{
/* getkey_bynames makes sure that only secret keys are returned
* if requested, thus we do not need to test again. With
* MARK_SECRET set (ie. option --with-secret) we have to test
* for a secret key, though. */
if (secret)
any_secret = 1;
else if (mark_secret)
any_secret = !agent_probe_any_secret_key (ctrl, keyblock);
else
any_secret = 0;
if (secret && !any_secret)
;/* Secret key listing requested but getkey_bynames failed. */
else
{
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);
}
listerr = list_keyblock (ctrl, keyblock, secret, any_secret,
opt.fingerprint, &listctx);
}
release_kbnode (keyblock);
}
while (!listerr && !getkey_next (ctrl, ctx, NULL, &keyblock));
getkey_end (ctrl, 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 no_local)
{
int rc = 0;
strlist_t sl;
GETKEY_CTX ctx = NULL;
KBNODE keyblock = NULL;
struct keylist_context listctx;
gpg_error_t listerr = 0;
memset (&listctx, 0, sizeof (listctx));
if (opt.check_sigs)
listctx.check_sigs = 1;
for (sl = names; sl && !listerr; sl = sl->next)
{
rc = get_best_pubkey_byname (ctrl,
no_local? GET_PUBKEY_NO_LOCAL
/* */: GET_PUBKEY_NORMAL,
&ctx, NULL, sl->d, &keyblock, 1);
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
{
listerr = list_keyblock (ctrl, keyblock, 0, 0,
opt.fingerprint, &listctx);
release_kbnode (keyblock);
}
while (!listerr && ctx && !getkey_next (ctrl, ctx, NULL, &keyblock));
getkey_end (ctrl, 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);
}
}
/* Various public key screenings. (Right now just ROCA). With
* COLON_MODE set the output is formatted for use in the compliance
* field of a colon listing.
*/
static void
print_pk_screening (PKT_public_key *pk, int colon_mode)
{
gpg_error_t err;
if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo))
{
err = screen_key_for_roca (pk->pkey[0]);
if (!err)
;
else if (gpg_err_code (err) == GPG_ERR_TRUE)
{
if (colon_mode)
es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001);
else
es_fprintf (es_stdout,
" Screening: ROCA vulnerability detected\n");
}
else if (!colon_mode)
es_fprintf (es_stdout, " Screening: [ROCA check failed: %s]\n",
gpg_strerror (err));
}
}
static void
print_capabilities (ctrl_t ctrl, 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_RENC)
es_putc ('r', es_stdout);
if ((use & PUBKEY_USAGE_TIME))
es_putc ('t', es_stdout);
if ((use & PUBKEY_USAGE_GROUP))
es_putc ('g', 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, 1, *i, &len, &seq, &crit)))
print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p);
seq = 0;
while ((p = enum_sig_subpkt (sig, 0, *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->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) |
(uid->flags.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
print_keygrip (const char *keygrip)
{
const char *s;
s = strchr (keygrip, ',');
if (s)
es_fprintf (es_stdout, " Keygrip = %.*s,\n%*s%s\n",
(int)(s-keygrip), keygrip, 16, "", s+1);
else
es_fprintf (es_stdout, " Keygrip = %s\n", keygrip);
}
/* If PK is given the output is written to a new file instead of
* stdout. */
static void
print_x509_notations (struct notation *nots, PKT_public_key *pk)
{
gpg_error_t err;
gpgrt_b64state_t state = NULL;
char hexfpr[2*4 + 1 + 2*MAX_FINGERPRINT_LEN+4+1];
char sha1[20];
estream_t fp;
for (; nots; nots = nots->next)
{
if (pk)
{
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1, nots->bdat, nots->blen);
bin2hex (sha1+16, 4, hexfpr);
hexfpr[2*4] = '-';
hexfingerprint (pk, hexfpr + 2*4+1, 2*MAX_FINGERPRINT_LEN);
strcat (hexfpr, ".pem");
fp = es_fopen (hexfpr, "w");
if (!fp)
{
err = gpg_err_code_from_syserror ();
goto b64fail;
}
}
else
fp = es_stdout;
state = gpgrt_b64enc_start (fp, "CERTIFICATE");
if (!state)
{
err = gpg_err_code_from_syserror ();
goto b64fail;
}
err = gpgrt_b64enc_write (state, nots->bdat, nots->blen);
if (err)
goto b64fail;
err = gpgrt_b64enc_finish (state);
if (err)
goto b64fail;
if (fp != es_stdout)
{
es_fclose (fp);
fp = NULL;
}
}
return;
b64fail:
log_error ("error writing base64 encoded notation: %s\n", gpg_strerror (err));
gpgrt_b64enc_finish (state);
if (fp && fp != es_stdout)
gpgrt_fcancel (fp);
}
/* Order two signatures. We first order by keyid and then by creation
* time. */
int
cmp_signodes (const void *av, const void *bv)
{
const kbnode_t an = *(const kbnode_t *)av;
const kbnode_t bn = *(const kbnode_t *)bv;
const PKT_signature *a;
const PKT_signature *b;
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;
/* Self-signatures are ordered first. */
if ((an->flag & NODFLG_MARK_B) && !(bn->flag & NODFLG_MARK_B))
return -1;
if (!(an->flag & NODFLG_MARK_B) && (bn->flag & NODFLG_MARK_B))
return 1;
/* then the keyids. (which are or course the same for self-sigs). */
i = keyid_cmp (a->keyid, b->keyid);
if (i)
return i;
/* Followed by creation time */
if (a->timestamp > b->timestamp)
return 1;
if (a->timestamp < b->timestamp)
return -1;
/* followed by the class in a way that a rev comes first. */
if (a->sig_class > b->sig_class)
return 1;
if (a->sig_class < b->sig_class)
return -1;
/* To make the sort stable we compare the entire structure as last resort. */
return memcmp (a, b, sizeof *a);
}
/* Given a domain name at NAME with length NAME, check whether this is
* a valid domain name and in that case return a malloced string ith
* the name. Escaped dots are ignored and removed from the result.
* Example: "example\.org" -> "example.org" Note that the input may
* not be Nul terminated. */
static char *
parse_trust_name (const char *name, size_t namelen)
{
char *buffer, *p;
p = buffer = xtrymalloc (namelen+1);
if (!buffer)
return NULL; /* Oops - caller needs to use some fallback */
for (; namelen; name++, namelen--)
{
if (*name == '\\' && namelen > 1 && name[1] == '.')
; /* Skip the escape character. */
else
*p++ = *name;
}
*p = 0;
if (!is_valid_domain_name (buffer))
{
xfree (buffer);
buffer = NULL;
}
return buffer;
}
void
print_revocation_reason_comment (const char *comment, size_t comment_len)
{
const byte *s, *s_lf;
size_t n, n_lf;
if (!comment || !comment_len)
return;
s = comment;
n = comment_len;
s_lf = NULL;
do
{
/* We don't want any empty lines, so we skip them. */
for (;n && *s == '\n'; s++, n--)
;
if (n)
{
s_lf = memchr (s, '\n', n);
n_lf = s_lf? s_lf - s : n;
es_fprintf (es_stdout, " %s",
_("revocation comment: "));
es_write_sanitized (es_stdout, s, n_lf, NULL, NULL);
es_putc ('\n', es_stdout);
s += n_lf; n -= n_lf;
}
} while (s_lf);
}
static void
print_revocation_reason (PKT_public_key *pk)
{
char *freeme;
const char *codestr;
if (!pk->revoked.got_reason)
return;
if (!pk->revoked.reason_code && !pk->revoked.reason_comment)
return; /* Do not print "revocation reason: No reason specified". */
codestr = revocation_reason_code_to_str (pk->revoked.reason_code, &freeme);
es_fprintf (es_stdout, " %s%s\n",
_("reason for revocation: "), codestr);
xfree (freeme);
print_revocation_reason_comment (pk->revoked.reason_comment,
pk->revoked.reason_comment_len);
}
/* Helper for list_keyblock_print. The caller must have set
* NODFLG_MARK_B to indicate self-signatures. */
static void
list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node,
struct keylist_context *listctx, PKT_public_key *lastpk)
{
PKT_signature *sig = node->pkt->pkt.signature;
int rc, sigrc;
char *sigstr;
char *reason_text = NULL;
char *reason_comment = NULL;
size_t reason_commentlen;
int reason_code = 0;
if (listctx->check_sigs)
{
rc = check_key_signature (ctrl, 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++;
return;
case GPG_ERR_DIGEST_ALGO:
case GPG_ERR_PUBKEY_ALGO:
if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS))
return;
/* fallthru. */
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
{
if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)
&& (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo)
== GPG_ERR_PUBKEY_ALGO)
|| gpg_err_code (openpgp_md_test_algo (sig->digest_algo)
== GPG_ERR_DIGEST_ALGO)
|| (sig->digest_algo == DIGEST_ALGO_SHA1
&& !(node->flag & NODFLG_MARK_B) /*no selfsig*/
&& !opt.flags.allow_weak_key_signatures)))
return;
rc = 0;
sigrc = ' ';
}
if (IS_KEY_REV (sig) || IS_SUBKEY_REV (sig) || IS_UID_REV (sig))
{
sigstr = "rev";
reason_code = get_revocation_reason (sig, &reason_text,
&reason_comment,
&reason_commentlen);
}
else if (IS_UID_SIG (sig))
sigstr = "sig";
else if (IS_SUBKEY_SIG (sig))
sigstr = "sig";
else if (IS_KEY_SIG (sig))
sigstr = "sig";
else
{
es_fprintf (es_stdout, "sig "
"[unexpected signature class 0x%02x]\n",
sig->sig_class);
return;
}
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 ((node->flag & NODFLG_MARK_B))
es_fputs (_("[self-signature]"), es_stdout);
else if (!opt.fast_list_mode )
{
size_t n;
char *p = get_user_id (ctrl, sig->keyid, &n, NULL);
print_utf8_buffer (es_stdout, p, n);
xfree (p);
}
if ((opt.list_options & LIST_SHOW_TRUSTSIG)
&& (sig->trust_depth || sig->trust_value || sig->trust_regexp))
{
es_fprintf (es_stdout, " [T=%d,%d", sig->trust_depth, sig->trust_value);
if (sig->trust_regexp)
{
size_t n = strlen (sig->trust_regexp);
char *tname = NULL;
if (!strncmp (sig->trust_regexp, "<[^>]+[@.]", 10)
&& n > 12 && !strcmp (sig->trust_regexp+n-2, ">$")
&& (tname=parse_trust_name (sig->trust_regexp+10, n-12)))
{
es_fprintf (es_stdout, ",\"%s", tname);
xfree (tname);
}
else
{
es_fputs (",R\"", es_stdout);
es_write_sanitized (es_stdout, sig->trust_regexp, n, "\"", NULL);
}
es_putc ('\"', es_stdout);
}
es_putc (']', es_stdout);
}
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) +
((opt.
list_options & LIST_SHOW_HIDDEN_NOTATIONS) ? 4 : 0));
if (sig->flags.notation
&& (opt.list_options
& (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS)))
{
struct notation *nots;
if ((IS_KEY_SIG (sig) || IS_SUBKEY_SIG (sig))
&& (nots = search_sig_notations (sig,
"x509certificate@pgp.com")))
{
if ((opt.list_options & LIST_STORE_X509_NOTATIONS))
print_x509_notations (nots, lastpk);
else
print_x509_notations (nots, NULL);
free_notation (nots);
}
}
if (sig->flags.pref_ks
&& (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 3, 0);
if (reason_text && (reason_code || reason_comment))
{
es_fprintf (es_stdout, " %s%s\n",
_("reason for revocation: "), reason_text);
print_revocation_reason_comment (reason_comment, reason_commentlen);
}
xfree (reason_text);
xfree (reason_comment);
/* fixme: check or list other sigs here */
}
static void
list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
struct keylist_context *listctx)
{
int rc;
kbnode_t node;
PKT_public_key *pk;
PKT_public_key *lastpk;
u32 *mainkid;
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;
mainkid = pk_keyid (pk);
lastpk = pk;
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 (ctrl, es_stdout, pk, secret);
if (fpr)
print_fingerprint (ctrl, NULL, pk, 0);
if (opt.with_keygrip && hexgrip)
print_keygrip (hexgrip);
if (serialno)
print_card_serialno (serialno);
if (opt.with_key_data)
print_key_data (pk);
if (opt.with_key_screening)
print_pk_screening (pk, 0);
if (opt.with_key_origin
&& (pk->keyorg || pk->keyupdate || pk->updateurl))
{
char updatestr[MK_DATESTR_SIZE];
es_fprintf (es_stdout, " origin=%s last=%s %s",
key_origin_string (pk->keyorg),
mk_datestr (updatestr, sizeof updatestr, pk->keyupdate),
pk->updateurl? "url=":"");
if (pk->updateurl)
print_utf8_string (es_stdout, pk->updateurl);
es_putc ('\n', es_stdout);
}
print_revokers (es_stdout, 0, pk);
for (node = keyblock; node; node = node->next)
{
if (is_deleted_kbnode (node))
continue;
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->flags.expired || uid->flags.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->flags.revoked || uid->flags.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.list_options & LIST_SHOW_PREF_VERBOSE))
show_preferences (uid, indent+2, 0, 1);
else if ((opt.list_options & LIST_SHOW_PREF))
show_preferences (uid, indent+2, 0, 0);
if (opt.with_wkd_hash)
{
char *mbox, *hash, *p;
char hashbuf[32];
mbox = mailbox_from_userid (uid->name, 0);
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.with_key_origin
&& (uid->keyorg || uid->keyupdate || uid->updateurl))
{
char updatestr[MK_DATESTR_SIZE];
es_fprintf (es_stdout, " %*sorigin=%s last=%s %s",
indent, "",
key_origin_string (uid->keyorg),
mk_datestr (updatestr, sizeof updatestr,
uid->keyupdate),
uid->updateurl? "url=":"");
if (uid->updateurl)
print_utf8_string (es_stdout, uid->updateurl);
es_putc ('\n', es_stdout);
}
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;
lastpk = pk2;
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 (ctrl, es_stdout, pk2, secret);
if (fpr > 1 || opt.with_subkey_fingerprint)
{
print_fingerprint (ctrl, NULL, pk2, 0);
if (serialno)
print_card_serialno (serialno);
}
if (opt.with_keygrip && hexgrip)
print_keygrip (hexgrip);
if (opt.with_key_data)
print_key_data (pk2);
if (opt.with_key_screening)
print_pk_screening (pk2, 0);
}
else if ((opt.list_sigs
|| (opt.list_options
& (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS)))
&& node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs)
{
kbnode_t n;
unsigned int sigcount = 0;
kbnode_t *sigarray;
unsigned int idx;
for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
sigcount++;
sigarray = xcalloc (sigcount, sizeof *sigarray);
sigcount = 0;
for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
{
if (keyid_eq (mainkid, n->pkt->pkt.signature->keyid))
n->flag |= NODFLG_MARK_B; /* Is a self-sig. */
else
n->flag &= ~NODFLG_MARK_B;
sigarray[sigcount++] = node = n;
}
/* Note that NODE is now at the last signature. */
if ((opt.list_options & LIST_SORT_SIGS))
qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes);
for (idx=0; idx < sigcount; idx++)
list_signature_print (ctrl, keyblock, sigarray[idx], listctx,
lastpk);
xfree (sigarray);
}
}
es_putc ('\n', es_stdout);
xfree (serialno);
xfree (hexgrip);
}
/* Do a simple key listing printing only the fingerprint and the mail
* address of valid keys. */
static void
list_keyblock_simple (ctrl_t ctrl, kbnode_t keyblock)
{
gpg_err_code_t ec;
kbnode_t kbctx;
kbnode_t node;
char hexfpr[2*MAX_FINGERPRINT_LEN+1];
char *mbox;
(void)ctrl;
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr);
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;
if (uid->attrib_data)
continue;
if ((uid->flags.expired || uid->flags.revoked)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
continue;
mbox = mailbox_from_userid (uid->name, 0);
if (!mbox)
{
ec = gpg_err_code_from_syserror ();
if (ec != GPG_ERR_EINVAL)
log_error ("error getting mailbox from user-id: %s\n",
gpg_strerror (ec));
continue;
}
es_fprintf (es_stdout, "%s %s\n", hexfpr, mbox);
xfree (mbox);
}
}
}
/* Print the revoker records. */
void
print_revokers (estream_t fp, int colon_mode, PKT_public_key * pk)
{
int i, j;
const byte *p;
if (!pk->revkey && pk->numrevkeys)
BUG ();
for (i = 0; i < pk->numrevkeys; i++)
{
if (colon_mode)
{
es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid);
p = pk->revkey[i].fpr;
for (j = 0; j < pk->revkey[i].fprlen; j++, p++)
es_fprintf (fp, "%02X", *p);
es_fprintf (fp, ":%02x%s:\n",
pk->revkey[i].class,
(pk->revkey[i].class & 0x40) ? "s" : "");
}
else
{
es_fprintf (fp, "%*s%s", 6, "", _("Revocable by: "));
p = pk->revkey[i].fpr;
es_write_hexstring (fp, pk->revkey[i].fpr, pk->revkey[i].fprlen,
0, NULL);
if ((pk->revkey[i].class & 0x40))
es_fprintf (fp, " %s", _("(sensitive)"));
/* Class bit 7 must always be set, bit 6 indicates sensitive
* and all others bits are reserved. */
if (!(pk->revkey[i].class & ~0x40)
|| (pk->revkey[i].class & ~(0x40|0x80)))
es_fprintf (fp, " (unknown class %02x)", pk->revkey[i].class);
es_fprintf (fp, "\n");
}
}
}
/* Print the compliance flags to field 18. PK is the public key.
* KEYLENGTH is the length of the key in bits and CURVENAME is either
* NULL or the name of the curve. The latter two args are here
* merely because the caller has already computed them. */
static void
print_compliance_flags (PKT_public_key *pk,
unsigned int keylength, const char *curvename)
{
int any = 0;
if (!keylength)
keylength = nbits_from_pk (pk);
if (pk->version == 5)
{
es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout);
any++;
}
if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
keylength, curvename))
{
es_fprintf (es_stdout, any ? " %s" : "%s",
gnupg_status_compliance_flag (CO_DE_VS));
any++;
}
if (opt.with_key_screening)
print_pk_screening (pk, 1+any);
}
/* 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;
unsigned int keylength;
char *curve = NULL;
const char *curvename = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE];
/* 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 primary 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, 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 (ctrl, pk, 0);
else
ownertrust_print = 0;
keylength = nbits_from_pk (pk);
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::",
keylength,
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 (ctrl, 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)
{
curve = openpgp_oid_to_str (pk->pkey[0]);
curvename = openpgp_oid_to_curve (curve, 0);
if (!curvename)
curvename = curve;
es_fputs (curvename, es_stdout);
}
else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
/* Note that Kyber should actually not appear here because it is
* the primary key and Kyber is not able to certify. But we
* prepare it here for future composite algorithms and in case
* of faulty packets. */
es_fputs (pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), es_stdout);
}
es_putc (':', es_stdout); /* End of field 17. */
print_compliance_flags (pk, keylength, curvename);
es_putc (':', es_stdout); /* End of field 18 (compliance). */
if (pk->keyupdate)
es_fputs (colon_strtime (pk->keyupdate), es_stdout);
es_putc (':', es_stdout); /* End of field 19 (last_update). */
es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":"");
if (pk->updateurl)
es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl),
":", NULL);
es_putc (':', es_stdout); /* End of field 20 (origin). */
if (pk->flags.revoked && pk->revoked.got_reason
&& (pk->revoked.reason_code || pk->revoked.reason_comment))
{
char *freeme;
const char *s;
size_t n;
s = revocation_reason_code_to_str (pk->revoked.reason_code, &freeme);
n = strlen (s);
es_write_sanitized (es_stdout, s, n, ":", NULL);
if (n && s[n-1] != '.')
es_putc ('.', es_stdout);
es_putc ('\\', es_stdout); /* C-style escaped colon. */
es_putc ('n', es_stdout);
es_write_sanitized (es_stdout, pk->revoked.reason_comment,
pk->revoked.reason_comment_len,
":", NULL);
xfree (freeme);
es_putc (':', es_stdout); /* End of field 21 (comment). */
}
es_putc ('\n', es_stdout);
print_revokers (es_stdout, 1, pk);
print_fingerprint (ctrl, 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->flags.revoked)
uid_validity = 'r';
else if (uid->flags.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, 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_fputs (":::::::::", es_stdout);
if (uid->keyupdate)
es_fputs (colon_strtime (uid->keyupdate), es_stdout);
es_putc (':', es_stdout); /* End of field 19 (last_update). */
es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":"");
if (uid->updateurl)
es_write_sanitized (es_stdout,
uid->updateurl, strlen (uid->updateurl),
":", NULL);
es_putc (':', es_stdout); /* End of field 20 (origin). */
es_putc ('\n', es_stdout);
if ((opt.list_options & (LIST_SHOW_PREF|LIST_SHOW_PREF_VERBOSE)))
show_preferences (uid, 0, 2, 0);
#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);
}
keylength = nbits_from_pk (pk2);
es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
keylength,
pk2->pubkey_algo,
(ulong) keyid2[0], (ulong) keyid2[1],
colon_datestr_from_pk (pk2),
colon_strtime (pk2->expiredate));
print_capabilities (ctrl, 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)
{
xfree (curve);
curve = openpgp_oid_to_str (pk2->pkey[0]);
curvename = openpgp_oid_to_curve (curve, 0);
if (!curvename)
curvename = curve;
es_fputs (curvename, es_stdout);
}
else if (pk2->pubkey_algo == PUBKEY_ALGO_KYBER)
{
es_fputs (pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
es_stdout);
}
es_putc (':', es_stdout); /* End of field 17. */
print_compliance_flags (pk2, keylength, curvename);
es_putc (':', es_stdout); /* End of field 18. */
es_putc ('\n', es_stdout);
print_fingerprint (ctrl, 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;
char *issuer_fpr = NULL;
char *reason_text = NULL;
char *reason_comment = NULL;
size_t reason_commentlen;
int reason_code = 0; /* Init to silence compiler warning. */
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
{
sigstr = "rev";
reason_code = get_revocation_reason (sig, &reason_text,
&reason_comment,
&reason_commentlen);
}
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;
es_fflush (es_stdout);
if (opt.no_sig_cache)
signer_pk = xmalloc_clear (sizeof (PKT_public_key));
rc = check_key_signature2 (ctrl, 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 = ' '; /* Note the fix-up below in --list-sigs mode. */
}
if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
{
int nouid;
siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid);
if (!opt.check_sigs && nouid)
sigrc = '?'; /* No key in local keyring. */
}
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 (reason_text)
es_fprintf (es_stdout, ",%02x", reason_code);
es_fputs ("::", es_stdout);
if (opt.no_sig_cache && opt.check_sigs && fprokay)
{
for (i = 0; i < fplen; i++)
es_fprintf (es_stdout, "%02X", fparray[i]);
}
else if ((issuer_fpr = issuer_fpr_string (sig)))
es_fputs (issuer_fpr, es_stdout);
es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
if (reason_comment)
{
es_fputs ("::::", es_stdout);
es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
":", NULL);
es_putc (':', es_stdout);
}
es_putc ('\n', es_stdout);
if (opt.show_subpackets)
print_subpackets_colon (sig);
/* fixme: check or list other sigs here */
xfree (reason_text);
xfree (reason_comment);
xfree (siguid);
xfree (issuer_fpr);
}
}
xfree (curve);
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->flags.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);
}
/* Note: If this function returns an error the caller is expected to
* honor this and stop all further processing. Any error returned
* will be a write error (to stdout) and a diagnostics is always
* printed using log_error. */
static gpg_error_t
list_keyblock (ctrl_t ctrl,
KBNODE keyblock, int secret, int has_secret, int fpr,
struct keylist_context *listctx)
{
gpg_error_t err = 0;
es_clearerr (es_stdout);
reorder_keyblock (keyblock);
if (list_filter.selkey)
{
int selected = 0;
struct impex_filter_parm_s parm;
parm.ctrl = ctrl;
for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
{
if (recsel_select (list_filter.selkey, impex_filter_getval, &parm))
{
selected = 1;
break;
}
}
if (!selected)
return 0; /* Skip this one. */
}
if (opt.with_colons)
list_keyblock_colon (ctrl, keyblock, secret, has_secret);
else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
{
if (!listctx->no_validity)
check_trustdb_stale (ctrl);
list_keyblock_simple (ctrl, keyblock);
}
else
list_keyblock_print (ctrl, keyblock, secret, fpr, listctx);
if (es_ferror (es_stdout))
err = gpg_error_from_syserror ();
if (secret && es_fflush (es_stdout) && !err)
err = gpg_error_from_syserror ();
if (err)
log_error (_("error writing to stdout: %s\n"), gpg_strerror (err));
return err;
}
/* Public function used by keygen to list a keyblock. If NO_VALIDITY
* is set the validity of a key is never shown. */
gpg_error_t
list_keyblock_direct (ctrl_t ctrl,
kbnode_t keyblock, int secret, int has_secret, int fpr,
int no_validity)
{
struct keylist_context listctx;
gpg_error_t err;
memset (&listctx, 0, sizeof (listctx));
listctx.no_validity = !!no_validity;
if (opt.check_sigs)
listctx.check_sigs = 1;
err = list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx);
keylist_context_release (&listctx);
return err;
}
/* 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 (ctrl_t ctrl, 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 (ctrl, primary_pk, pk->main_keyid);
print_fingerprint (ctrl, 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);
if (opt.with_v5_fingerprint && pk->version == 4)
{
char *v5fpr = v5hexfingerprint (pk, NULL, 0);
es_fprintf (fp, "\nfp2:::::::::%s:", v5fpr);
xfree (v5fpr);
}
}
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%10))
tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, "");
else if (!(i%5))
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
*
* pub rsa2048 2017-12-31 [SC] [expires: 2028-12-31]
* 80615870F5BAD690333686D0F2AD85AC1E42B3671122334455
*
* 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 (ctrl_t ctrl, 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 (pk->flags.primary
&& !(openpgp_pk_algo_usage (pk->pubkey_algo)
& (PUBKEY_USAGE_CERT| PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)))
{
/* A primary key which is really not capable to sign. */
tty_fprintf (fp, " [INVALID_ALGO]");
}
else if ((opt.list_options & LIST_SHOW_USAGE))
{
tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0));
}
if (pk->flags.primary && (opt.list_options & LIST_SHOW_OWNERTRUST))
{
tty_fprintf (fp, " [%s]", get_ownertrust_string (ctrl, 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 (pk->pubkey_algo >= 100)
tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo);
tty_fprintf (fp, "\n");
if (pk->flags.primary && pk_is_disabled (pk))
es_fprintf (es_stdout, " *** %s\n", _("This key has been disabled"));
/* 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 (ctrl, fp, pk, 20);
/* Print the revocation reason. */
if (pk->flags.revoked)
print_revocation_reason (pk);
}
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;
if (! gnupg_fd_valid (fd))
log_fatal ("attribute-fd is invalid: %s\n", strerror (errno));
#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/pubkey-enc.c b/g10/pubkey-enc.c
index d9a68d587..396e125d9 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -1,589 +1,589 @@
/* pubkey-enc.c - Process a public key encoded packet.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "keydb.h"
#include "trustdb.h"
#include "../common/status.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#include "pkglue.h"
#include "call-agent.h"
#include "../common/host2net.h"
#include "../common/compliance.h"
static gpg_error_t get_it (ctrl_t ctrl, struct seskey_enc_list *k,
DEK *dek, PKT_public_key *sk, u32 *keyid);
/* Check that the given algo is mentioned in one of the valid user-ids. */
static int
is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
{
kbnode_t k;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = k->pkt->pkt.user_id;
prefitem_t *prefs = uid->prefs;
if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired)
{
for (; prefs->type; prefs++)
if (prefs->type == type && prefs->value == algo)
return 1;
}
}
}
return 0;
}
/*
* Get the session key from a pubkey enc packet and return it in DEK,
* which should have been allocated in secure memory by the caller.
*/
gpg_error_t
get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek)
{
PKT_public_key *sk = NULL;
gpg_error_t err;
void *enum_context = NULL;
u32 keyid[2];
int search_for_secret_keys = 1;
struct seskey_enc_list *k;
if (DBG_CLOCK)
log_clock ("get_session_key enter");
while (search_for_secret_keys)
{
sk = xmalloc_clear (sizeof *sk);
err = enum_secret_keys (ctrl, &enum_context, sk);
if (err)
break;
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
sk->pubkey_algo, 0,
sk->pkey, nbits_from_pk (sk), NULL))
{
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
keystr_from_pk (sk),
gnupg_compliance_option_string (opt.compliance));
continue;
}
/* FIXME: The list needs to be sorted so that we try the keys in
* an appropriate order. For example:
* - On-disk keys w/o protection
* - On-disk keys with a cached passphrase
* - On-card keys of an active card
* - On-disk keys with protection
* - On-card keys from cards which are not plugged it. Here a
* cancel-all button should stop asking for other cards.
* Without any anonymous keys the sorting can be skipped.
*/
for (k = list; k; k = k->next)
{
if (k->u_sym)
continue;
if (!(k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
|| k->u.pub.pubkey_algo == PUBKEY_ALGO_ECDH
|| k->u.pub.pubkey_algo == PUBKEY_ALGO_KYBER
|| k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA
|| k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA_E
|| k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL))
continue;
if (openpgp_pk_test_algo2 (k->u.pub.pubkey_algo, PUBKEY_USAGE_ENC))
continue;
if (sk->pubkey_algo != k->u.pub.pubkey_algo)
continue;
keyid_from_pk (sk, keyid);
if (!k->u.pub.keyid[0] && !k->u.pub.keyid[1])
{
if (opt.skip_hidden_recipients)
continue;
if (!opt.quiet)
log_info (_("anonymous recipient; trying secret key %s ...\n"),
keystr (keyid));
}
else if (opt.try_all_secrets
|| (k->u.pub.keyid[0] == keyid[0]
&& k->u.pub.keyid[1] == keyid[1]))
{
if (!opt.quiet && !(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
log_info (_("used key is not marked for encryption use.\n"));
}
else
continue;
err = get_it (ctrl, k, dek, sk, keyid);
k->result = err;
if (!err)
{
if (!opt.quiet && !k->u.pub.keyid[0] && !k->u.pub.keyid[1])
{
log_info (_("okay, we are the anonymous recipient.\n"));
if (!(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
log_info (_("used key is not marked for encryption use.\n")
);
}
search_for_secret_keys = 0;
break;
}
else if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
{
search_for_secret_keys = 0;
break; /* Don't try any more secret keys. */
}
}
}
enum_secret_keys (ctrl, &enum_context, NULL); /* free context */
if (gpg_err_code (err) == GPG_ERR_EOF)
{
err = gpg_error (GPG_ERR_NO_SECKEY);
/* Return the last specific error, if any. */
for (k = list; k; k = k->next)
if (k->result != -1)
err = k->result;
}
if (DBG_CLOCK)
log_clock ("get_session_key leave");
return err;
}
/* Build an SEXP to gpg-agent, for PKDECRYPT command. */
static gpg_error_t
ecdh_sexp_build (gcry_sexp_t *r_s_data, struct seskey_enc_list *enc,
PKT_public_key *sk)
{
gpg_error_t err;
const unsigned char *kdf_params_spec;
byte fp[MAX_FINGERPRINT_LEN];
int keywrap_cipher_algo;
int kdf_hash_algo;
unsigned char *kdf_params = NULL;
size_t kdf_params_len = 0;
fingerprint_from_pk (sk, fp, NULL);
err = ecc_build_kdf_params (&kdf_params, &kdf_params_len,
&kdf_params_spec, sk->pkey, fp);
if (err)
return err;
keywrap_cipher_algo = kdf_params_spec[3];
kdf_hash_algo = kdf_params_spec[2];
if (!enc->u.pub.data[0] || !enc->u.pub.data[1])
{
xfree (kdf_params);
return gpg_error (GPG_ERR_BAD_MPI);
}
err = gcry_sexp_build (r_s_data, NULL,
"(enc-val(ecc(c%d)(h%d)(e%m)(s%m)(kdf-params%b)))",
keywrap_cipher_algo, kdf_hash_algo,
enc->u.pub.data[0], enc->u.pub.data[1],
(int)kdf_params_len, kdf_params);
xfree (kdf_params);
return err;
}
static gpg_error_t
get_it (ctrl_t ctrl,
struct seskey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
{
gpg_error_t err;
byte *frame = NULL;
unsigned int frameidx;
size_t nframe;
u16 csum, csum2;
int padding;
gcry_sexp_t s_data;
char *desc;
char *keygrip;
if (DBG_CLOCK)
log_clock ("decryption start");
log_assert (!enc->u_sym);
/* Get the keygrip. */
err = hexkeygrip_from_pk (sk, &keygrip);
if (err)
goto leave;
/* Convert the data to an S-expression. */
if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
|| sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
{
if (!enc->u.pub.data[0] || !enc->u.pub.data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
enc->u.pub.data[0], enc->u.pub.data[1]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
|| sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
{
if (!enc->u.pub.data[0])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
enc->u.pub.data[0]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
err = ecdh_sexp_build (&s_data, enc, sk);
else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
char fixedinfo[1+MAX_FINGERPRINT_LEN];
int fixedlen;
if ((opt.compat_flags & COMPAT_T7014_OLD))
{
/* Temporary use for tests with original test vectors. */
fixedinfo[0] = 0x69;
fixedlen = 1;
}
else
{
fixedinfo[0] = enc->u.pub.seskey_algo;
v5_fingerprint_from_pk (sk, fixedinfo+1, NULL);
fixedlen = 33;
}
if (!enc->u.pub.data[0] || !enc->u.pub.data[1] || !enc->u.pub.data[2])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL,
"(enc-val(pqc(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))",
enc->u.pub.data[0],
enc->u.pub.data[1],
enc->u.pub.data[2],
enc->u.pub.seskey_algo, fixedlen, fixedinfo);
}
else
err = gpg_error (GPG_ERR_BUG);
if (err)
goto leave;
/* Decrypt. */
desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pkdecrypt (NULL, keygrip,
desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
s_data, &frame, &nframe, &padding);
xfree (desc);
gcry_sexp_release (s_data);
if (err)
goto leave;
/* Now get the DEK (data encryption key) from the frame
*
* Old versions encode the DEK in this format (msb is left):
*
* 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
*
* Later versions encode the DEK like this:
*
* 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
*
* (mpi_get_buffer already removed the leading zero).
*
* RND are non-zero randow bytes.
* A is the cipher algorithm
* DEK is the encryption key (session key) with length k
* CSUM
*/
if (DBG_CRYPTO)
log_printhex (frame, nframe, "DEK frame:");
frameidx = 0;
if (sk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
if (nframe != 32 && opt.flags.require_pqc_encryption)
{
log_info (_("WARNING: session key is not quantum-resistant\n"));
}
dek->keylen = nframe;
dek->algo = enc->u.pub.seskey_algo;
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
/* Now the frame are the bytes decrypted but padded session key. */
if (!nframe || nframe <= 8
|| frame[nframe-1] > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
nframe -= frame[nframe-1]; /* Remove padding. */
if (4 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe - 3;
dek->algo = frame[0];
frameidx = 1;
}
else
{
if (padding)
{
if (7 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to
* the way we encode the output in libgcrypt as an MPI we
* are not able to encode that leading zero. However, when
* using a Smartcard we are doing it the right way and
* therefore we have to skip the zero. This should be fixed
* in gpg-agent of course. */
frameidx = 0;
if (!frame[frameidx])
frameidx++;
if (frame[frameidx] == 1 && frame[nframe - 1] == 2)
{
log_info (_("old encoding of the DEK is not supported\n"));
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (frame[frameidx] != 2) /* Something went wrong. */
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* Skip the random bytes. */
for (frameidx++; frameidx < nframe && frame[frameidx]; frameidx++)
;
frameidx++; /* Skip the zero byte. */
}
if (frameidx + 4 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe - (frameidx + 1) - 2;
dek->algo = frame[frameidx++];
}
/* Check whether we support the ago. */
err = openpgp_cipher_test_algo (dek->algo);
if (err)
{
if (!opt.quiet && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
{
log_info (_("cipher algorithm %d%s is unknown or disabled\n"),
dek->algo,
dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : "");
}
dek->algo = 0;
goto leave;
}
if (dek->keylen != openpgp_cipher_get_algo_keylen (dek->algo))
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* Copy the key to DEK and compare the checksum if needed. */
/* We use the frameidx as flag for the need of a checksum. */
memcpy (dek->key, frame + frameidx, dek->keylen);
if (frameidx)
{
csum = buf16_to_u16 (frame+nframe-2);
for (csum2 = 0, frameidx = 0; frameidx < dek->keylen; frameidx++)
csum2 += dek->key[frameidx];
if (csum != csum2)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
}
if (DBG_CLOCK)
log_clock ("decryption ready");
if (DBG_CRYPTO)
log_printhex (dek->key, dek->keylen, "DEK is:");
/* Check that the algo is in the preferences and whether it has
* expired. Also print a status line with the key's fingerprint. */
{
PKT_public_key *pk = NULL;
PKT_public_key *mainpk = NULL;
- KBNODE pkb = get_pubkeyblock_ext (ctrl, keyid, GET_PUBKEYBLOCK_FLAG_ADSK);
+ kbnode_t pkb = get_pubkeyblock_ext (ctrl, keyid, GETKEY_ALLOW_ADSK);
if (!pkb)
{
err = gpg_error (GPG_ERR_UNEXPECTED);
log_info ("oops: public key not found for preference check\n");
}
else if (pkb->pkt->pkt.public_key->selfsigversion > 3
&& dek->algo != CIPHER_ALGO_3DES
&& !opt.quiet
&& !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo))
log_info (_("WARNING: cipher algorithm %s not found in recipient"
" preferences\n"), openpgp_cipher_algo_name (dek->algo));
/* if (!err && 25519 && openpgp_oidbuf_is_ed25519 (curve, len)) */
/* log_info ("Note: legacy OID was used for cv25519\n"); */
if (!err)
{
kbnode_t k;
int first = 1;
for (k = pkb; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
u32 aki[2];
if (first)
{
first = 0;
mainpk = k->pkt->pkt.public_key;
}
keyid_from_pk (k->pkt->pkt.public_key, aki);
if (aki[0] == keyid[0] && aki[1] == keyid[1])
{
pk = k->pkt->pkt.public_key;
break;
}
}
}
if (!pk)
BUG ();
if (pk->expiredate && pk->expiredate <= make_timestamp ())
{
log_info (_("Note: secret key %s expired at %s\n"),
keystr (keyid), asctimestamp (pk->expiredate));
}
}
if (pk && !(pk->pubkey_usage & PUBKEY_USAGE_ENC)
&& (pk->pubkey_usage & PUBKEY_USAGE_RENC))
{
log_info (_("Note: ADSK key has been used for decryption"));
log_printf ("\n");
}
if (pk && pk->flags.revoked)
{
log_info (_("Note: key has been revoked"));
log_printf ("\n");
show_revocation_reason (ctrl, pk, 1);
}
if (is_status_enabled () && pk && mainpk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
hexfingerprint (pk, pkhex, sizeof pkhex);
hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
/* Note that we do not want to create a trustdb just for
* getting the ownertrust: If there is no trustdb there can't
* be an ultimately trusted key anyway and thus the ownertrust
* value is irrelevant. */
write_status_printf (STATUS_DECRYPTION_KEY, "%s %s %c",
pkhex, mainpkhex,
get_ownertrust_info (ctrl, mainpk, 1));
}
release_kbnode (pkb);
err = 0;
}
leave:
xfree (frame);
xfree (keygrip);
return err;
}
/*
* Get the session key from the given string.
* String is supposed to be formatted as this:
* <algo-id>:<even-number-of-hex-digits>
*/
gpg_error_t
get_override_session_key (DEK *dek, const char *string)
{
const char *s;
int i;
if (!string)
return GPG_ERR_BAD_KEY;
dek->algo = atoi (string);
if (dek->algo < 1)
return GPG_ERR_BAD_KEY;
if (!(s = strchr (string, ':')))
return GPG_ERR_BAD_KEY;
s++;
for (i = 0; i < DIM (dek->key) && *s; i++, s += 2)
{
int c = hextobyte (s);
if (c == -1)
return GPG_ERR_BAD_KEY;
dek->key[i] = c;
}
if (*s)
return GPG_ERR_BAD_KEY;
dek->keylen = i;
return 0;
}
diff --git a/g10/skclist.c b/g10/skclist.c
index fe77aaede..6b16879ce 100644
--- a/g10/skclist.c
+++ b/g10/skclist.c
@@ -1,575 +1,576 @@
/* skclist.c - Build a list of secret keys
* Copyright (C) 1998, 1999, 2000, 2001, 2006,
* 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "keydb.h"
#include "../common/util.h"
#include "../common/i18n.h"
#include "keyserver-internal.h"
#include "call-agent.h"
/* Return true if Libgcrypt's RNG is in faked mode. */
int
random_is_faked (void)
{
return !!gcry_control (GCRYCTL_FAKED_RANDOM_P, 0);
}
void
release_sk_list (SK_LIST sk_list)
{
SK_LIST sk_rover;
for (; sk_list; sk_list = sk_rover)
{
sk_rover = sk_list->next;
free_public_key (sk_list->pk);
xfree (sk_list);
}
}
/* Check that we are only using keys which don't have
* the string "(insecure!)" or "not secure" or "do not use"
* in one of the user ids. */
static int
is_insecure (ctrl_t ctrl, PKT_public_key *pk)
{
u32 keyid[2];
KBNODE node = NULL, u;
int insecure = 0;
keyid_from_pk (pk, keyid);
node = get_pubkeyblock (ctrl, keyid);
for (u = node; u; u = u->next)
{
if (u->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *id = u->pkt->pkt.user_id;
if (id->attrib_data)
continue; /* skip attribute packets */
if (strstr (id->name, "(insecure!)")
|| strstr (id->name, "not secure")
|| strstr (id->name, "do not use")
|| strstr (id->name, "(INSECURE!)"))
{
insecure = 1;
break;
}
}
}
release_kbnode (node);
return insecure;
}
static int
key_present_in_sk_list (SK_LIST sk_list, PKT_public_key *pk)
{
for (; sk_list; sk_list = sk_list->next)
{
if (!cmp_public_keys (sk_list->pk, pk))
return 0;
}
return -1;
}
static int
is_duplicated_entry (strlist_t list, strlist_t item)
{
for (; list && list != item; list = list->next)
{
if (!strcmp (list->d, item->d))
return 1;
}
return 0;
}
gpg_error_t
build_sk_list (ctrl_t ctrl,
strlist_t locusr, SK_LIST *ret_sk_list, unsigned int use)
{
gpg_error_t err;
SK_LIST sk_list = NULL;
/* XXX: Change this function to use get_pubkeys instead of
getkey_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 (!locusr) /* No user ids given - use the card key or the default key. */
{
struct agent_card_info_s info;
PKT_public_key *pk;
memset (&info, 0, sizeof(info));
pk = xmalloc_clear (sizeof *pk);
pk->req_usage = use;
/* Check if a card is available. If any, use the key as a hint. */
err = agent_scd_serialno (NULL, NULL);
if (!err)
{
err = agent_scd_getattr ("KEY-FPR", &info);
if (err)
log_error ("error retrieving key fingerprint from card: %s\n",
gpg_strerror (err));
}
err = get_seckey_default_or_card (ctrl, pk,
info.fpr1len? info.fpr1 : NULL,
info.fpr1len);
if (err)
{
free_public_key (pk);
pk = NULL;
log_error ("no default secret key: %s\n", gpg_strerror (err));
write_status_text (STATUS_INV_SGNR, get_inv_recpsgnr_code (err));
}
else if ((err = openpgp_pk_test_algo2 (pk->pubkey_algo, use)))
{
free_public_key (pk);
pk = NULL;
log_error ("invalid default secret key: %s\n", gpg_strerror (err));
write_status_text (STATUS_INV_SGNR, get_inv_recpsgnr_code (err));
}
else
{
SK_LIST r;
if (random_is_faked () && !is_insecure (ctrl, pk))
{
log_info (_("key is not flagged as insecure - "
"can't use it with the faked RNG!\n"));
free_public_key (pk);
pk = NULL;
write_status_text (STATUS_INV_SGNR,
get_inv_recpsgnr_code (GPG_ERR_NOT_TRUSTED));
}
else
{
r = xmalloc (sizeof *r);
r->pk = pk;
pk = NULL;
r->next = sk_list;
r->mark = 0;
sk_list = r;
}
}
}
else /* Check the given user ids. */
{
strlist_t locusr_orig = locusr;
for (; locusr; locusr = locusr->next)
{
PKT_public_key *pk;
err = 0;
/* Do an early check against duplicated entries. However
* this won't catch all duplicates because the user IDs may
* be specified in different ways. */
if (is_duplicated_entry (locusr_orig, locusr))
{
log_info (_("skipped \"%s\": duplicated\n"), locusr->d);
continue;
}
pk = xmalloc_clear (sizeof *pk);
pk->req_usage = use;
if ((err = getkey_byname (ctrl, NULL, pk, locusr->d, 1, NULL)))
{
free_public_key (pk);
pk = NULL;
log_error (_("skipped \"%s\": %s\n"),
locusr->d, gpg_strerror (err));
write_status_text_and_buffer
(STATUS_INV_SGNR, get_inv_recpsgnr_code (err),
locusr->d, strlen (locusr->d), -1);
}
else if (!key_present_in_sk_list (sk_list, pk))
{
free_public_key (pk);
pk = NULL;
log_info (_("skipped: secret key already present\n"));
}
else if ((err = openpgp_pk_test_algo2 (pk->pubkey_algo, use)))
{
free_public_key (pk);
pk = NULL;
log_error ("skipped \"%s\": %s\n", locusr->d, gpg_strerror (err));
write_status_text_and_buffer
(STATUS_INV_SGNR, get_inv_recpsgnr_code (err),
locusr->d, strlen (locusr->d), -1);
}
else
{
SK_LIST r;
if (pk->version == 4 && (use & PUBKEY_USAGE_SIG)
&& pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
{
log_info (_("skipped \"%s\": %s\n"), locusr->d,
_("this is a PGP generated Elgamal key which"
" is not secure for signatures!"));
free_public_key (pk);
pk = NULL;
write_status_text_and_buffer
(STATUS_INV_SGNR,
get_inv_recpsgnr_code (GPG_ERR_WRONG_KEY_USAGE),
locusr->d, strlen (locusr->d), -1);
}
else if (random_is_faked () && !is_insecure (ctrl, pk))
{
log_info (_("key is not flagged as insecure - "
"can't use it with the faked RNG!\n"));
free_public_key (pk);
pk = NULL;
write_status_text_and_buffer
(STATUS_INV_SGNR,
get_inv_recpsgnr_code (GPG_ERR_NOT_TRUSTED),
locusr->d, strlen (locusr->d), -1);
}
else
{
r = xmalloc (sizeof *r);
r->pk = pk;
pk = NULL;
r->next = sk_list;
r->mark = 0;
sk_list = r;
}
}
}
}
if (!err && !sk_list)
{
log_error ("no valid signators\n");
write_status_text (STATUS_NO_SGNR, "0");
err = gpg_error (GPG_ERR_NO_USER_ID);
}
if (err)
release_sk_list (sk_list);
else
*ret_sk_list = sk_list;
return err;
}
/* 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 (context)
* 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;
* sk = xmalloc_clear (sizeof (*sk));
* }
*
* // Release any resources used by CTX.
* enum_secret_keys (&ctx, NULL);
*
* 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;
int cardkey;
kbnode_t keyblock;
struct
{
int eof;
int state;
int cardkey_done;
strlist_t sl;
keypair_info_t card_keyinfo;
keypair_info_t card_keyinfo_list;
char fpr2[2 * MAX_FINGERPRINT_LEN + 2 ];
kbnode_t keyblock;
kbnode_t node;
getkey_ctx_t ctx;
SK_LIST results;
} *c = *context;
#if MAX_FINGERPRINT_LEN < KEYGRIP_LEN
# error buffer too short for this configuration
#endif
if (!c)
{
/* Make a new context. */
c = xtrycalloc (1, sizeof *c);
if (!c)
{
err = gpg_error_from_syserror ();
free_public_key (sk);
return err;
}
*context = c;
}
if (!sk)
{
/* Free the context. */
free_keypair_info (c->card_keyinfo_list);
release_sk_list (c->results);
release_kbnode (c->keyblock);
getkey_end (ctrl, c->ctx);
xfree (c);
*context = NULL;
return 0;
}
if (c->eof)
{
free_public_key (sk);
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;
cardkey = 0;
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 list of card keys to try. */
c->card_keyinfo_list = NULL;
err = agent_scd_serialno (NULL, NULL);
if (!err)
{
err = agent_scd_keyinfo (NULL, GCRY_PK_USAGE_ENCR,
&c->card_keyinfo_list);
}
c->card_keyinfo = c->card_keyinfo_list;
c->state++;
break;
case 4: /* Get next item from card keyinfo. */
if (c->card_keyinfo)
{
const char *s;
int i;
/* Get the key using the keygrip. */
c->fpr2[0] = '&';
for (i=1, s = c->card_keyinfo->keygrip;
(*s && *s != ' '
&& i < sizeof c->fpr2 - 2);
s++, i++)
c->fpr2[i] = *s;
c->fpr2[i] = 0;
name = c->fpr2;
cardkey = 1;
c->card_keyinfo = c->card_keyinfo->next;
}
else
c->state++;
break;
case 5: /* Init search context to enum all secret keys. */
- err = getkey_bynames (ctrl, &c->ctx, NULL, NULL, 1,
+ err = getkey_bynames (ctrl, &c->ctx, NULL, NULL,
+ GETKEY_WANT_SECRET,
&keyblock);
if (err)
{
release_kbnode (keyblock);
keyblock = NULL;
getkey_end (ctrl, c->ctx);
c->ctx = NULL;
}
c->state++;
break;
case 6: /* Get next item from the context. */
if (c->ctx)
{
err = getkey_next (ctrl, c->ctx, NULL, &keyblock);
if (err)
{
release_kbnode (keyblock);
keyblock = NULL;
getkey_end (ctrl, c->ctx);
c->ctx = NULL;
}
}
else
c->state++;
break;
default: /* No more names to check - stop. */
c->eof = 1;
free_public_key (sk);
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)
{
struct agent_card_info_s cinfo = { 0 };
/* 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;
/* If this was a card key we might not yet have the
* public key for it. Thus check whether the card
* can return the fingerprint of the encryption key
* and we can then find the public key via LDAP. */
if (cardkey && !c->cardkey_done
&& gpg_err_code (err) == GPG_ERR_NO_SECKEY
&& !agent_scd_getattr ("KEY-FPR", &cinfo)
&& cinfo.fpr2len)
{
/* Note that this code does not handle the case
* for two readers having both openpgp
* encryption keys. only one will be tried. */
c->cardkey_done = 1;
if (opt.debug)
log_debug ("using LDAP to find public key"
" for current card\n");
if (!keyserver_import_fpr
(ctrl, cinfo.fpr2, cinfo.fpr2len, opt.keyserver,
KEYSERVER_IMPORT_FLAG_LDAP))
{
char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1];
bin2hex (cinfo.fpr2, cinfo.fpr2len, fpr_string);
err = getkey_byname (ctrl, NULL, NULL, fpr_string, 1,
&c->keyblock);
if (err)
{
release_kbnode (c->keyblock);
c->keyblock = NULL;
}
else
c->node = c->keyblock;
}
}
}
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)
{
SK_LIST r;
/* Skip this candidate if it's already enumerated. */
for (r = c->results; r; r = r->next)
if (!cmp_public_keys (r->pk, c->node->pkt->pkt.public_key))
break;
if (r)
continue;
copy_public_key (sk, c->node->pkt->pkt.public_key);
c->node = c->node->next;
r = xtrycalloc (1, sizeof (*r));
if (!r)
{
err = gpg_error_from_syserror ();
free_public_key (sk);
return err;
}
r->pk = sk;
r->next = c->results;
c->results = r;
return 0; /* Found. */
}
}
/* Dispose the keyblock and continue. */
release_kbnode (c->keyblock);
c->keyblock = NULL;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Dec 6, 10:41 PM (1 d, 12 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e2/67/ae1d5d7eed844b59aa3af083d5d6

Event Timeline