/* keylist.c - Listing keys.
- Copyright (C) 2000 Werner Koch (dd9jn)
- Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007,
- 2008, 2009 g10 Code GmbH
-
- This file is part of GPGME.
-
- GPGME is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of
- the License, or (at your option) any later version.
-
- GPGME 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2000 Werner Koch (dd9jn)
+ * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007,
+ * 2008, 2009 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*/
#if HAVE_CONFIG_H
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <limits.h>
/* Suppress warning for accessing deprecated member "class". */
#define _GPGME_IN_GPGME
{
struct _gpgme_op_keylist_result result;
+ /* The error code from ERROR keydb_search. */
+ gpgme_error_t keydb_search_err;
+
gpgme_key_t tmp_key;
/* This points to the last uid in tmp_key. */
op_data_t opd;
gpgme_error_t err;
- TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx);
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx, "");
err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
opd = hook;
if (err || !opd)
{
- TRACE_SUC0 ("result=(null)");
+ TRACE_SUC ("result=(null)");
return NULL;
}
- TRACE_LOG1 ("truncated = %i", opd->result.truncated);
+ TRACE_LOG ("truncated = %i", opd->result.truncated);
- TRACE_SUC1 ("result=%p", &opd->result);
+ TRACE_SUC ("result=%p", &opd->result);
return &opd->result;
}
void *hook;
op_data_t opd;
+ (void)args;
+
err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
opd = hook;
if (err)
opd->result.truncated = 1;
break;
+ case GPGME_STATUS_ERROR:
+ err = _gpgme_parse_failure (args);
+ if (!opd->keydb_search_err && !strcmp (args, "keydb_search"))
+ opd->keydb_search_err = err;
+ err = 0;
+ break;
+
default:
break;
}
- return 0;
+ return err;
}
\f
}
+static gpgme_keyorg_t
+parse_keyorg (const char *string)
+{
+ switch (atoi (string))
+ {
+ case 0: return GPGME_KEYORG_UNKNOWN;
+ case 1:
+ case 2:
+ return GPGME_KEYORG_KS;
+ case 3: return GPGME_KEYORG_DANE;
+ case 4: return GPGME_KEYORG_WKD;
+ case 5: return GPGME_KEYORG_URL;
+ case 6: return GPGME_KEYORG_FILE;
+ case 7: return GPGME_KEYORG_SELF;
+ default: return GPGME_KEYORG_OTHER;
+ }
+}
+
+
/* Parse field 15 of a secret key or subkey. This fields holds a
reference to smartcards. FIELD is the content of the field and we
are allowed to modify it. */
static gpg_error_t
-parse_sec_field15 (gpgme_subkey_t subkey, char *field)
+parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field)
{
if (!*field)
; /* Empty. */
{
/* This is a stub for an offline key. We reset the SECRET flag
of the subkey here. Note that the secret flag of the entire
- key will be true even then. */
+ key will be true even then. We even explicitly set
+ key->secret to make it works for GPGME_KEYLIST_MODE_WITH_SECRET. */
subkey->secret = 0;
+ key->secret = 1;
}
else if (strchr ("01234567890ABCDEFabcdef", *field))
{
/* Fields starts with a hex digit; thus it is a serial number. */
+ key->secret = 1;
subkey->is_cardkey = 1;
subkey->card_number = strdup (field);
if (!subkey->card_number)
return gpg_error_from_syserror ();
}
+ else if (*field == '+')
+ {
+ key->secret = 1;
+ subkey->secret = 1;
+ }
else
{
/* RFU. */
}
+/* Parse a tfs record. */
+static gpg_error_t
+parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield)
+{
+ gpg_error_t err;
+ gpgme_tofu_info_t ti;
+ unsigned long uval;
+
+ /* We add only the first TOFU record in case future versions emit
+ * several. */
+ if (uid->tofu)
+ return 0;
+
+ /* Check that we have enough fields and that the version is supported. */
+ if (nfield < 8 || atoi(field[1]) != 1)
+ return trace_gpg_error (GPG_ERR_INV_ENGINE);
+
+ ti = calloc (1, sizeof *ti);
+ if (!ti)
+ return gpg_error_from_syserror ();
+
+ /* Note that we allow a value of up to 7 which is what we can store
+ * in the ti->validity. */
+ err = _gpgme_strtoul_field (field[2], &uval);
+ if (err || uval > 7)
+ goto inv_engine;
+ ti->validity = uval;
+
+ /* Parse the sign-count. */
+ err = _gpgme_strtoul_field (field[3], &uval);
+ if (err)
+ goto inv_engine;
+ if (uval > USHRT_MAX)
+ uval = USHRT_MAX;
+ ti->signcount = uval;
+
+ /* Parse the encr-count. */
+ err = _gpgme_strtoul_field (field[4], &uval);
+ if (err)
+ goto inv_engine;
+ if (uval > USHRT_MAX)
+ uval = USHRT_MAX;
+ ti->encrcount = uval;
+
+ /* Parse the policy. */
+ if (!strcmp (field[5], "none"))
+ ti->policy = GPGME_TOFU_POLICY_NONE;
+ else if (!strcmp (field[5], "auto"))
+ ti->policy = GPGME_TOFU_POLICY_AUTO;
+ else if (!strcmp (field[5], "good"))
+ ti->policy = GPGME_TOFU_POLICY_GOOD;
+ else if (!strcmp (field[5], "bad"))
+ ti->policy = GPGME_TOFU_POLICY_BAD;
+ else if (!strcmp (field[5], "ask"))
+ ti->policy = GPGME_TOFU_POLICY_ASK;
+ else /* "unknown" and invalid policy strings. */
+ ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
+
+ /* Parse first and last seen timestamps. */
+ err = _gpgme_strtoul_field (field[6], &uval);
+ if (err)
+ goto inv_engine;
+ ti->signfirst = uval;
+ err = _gpgme_strtoul_field (field[7], &uval);
+ if (err)
+ goto inv_engine;
+ ti->signlast = uval;
+
+ if (nfield > 9)
+ {
+ /* This condition is only to allow for gpg 2.1.15 - can
+ * eventually be removed. */
+ err = _gpgme_strtoul_field (field[8], &uval);
+ if (err)
+ goto inv_engine;
+ ti->encrfirst = uval;
+ err = _gpgme_strtoul_field (field[9], &uval);
+ if (err)
+ goto inv_engine;
+ ti->encrlast = uval;
+ }
+
+ /* Ready. */
+ uid->tofu = ti;
+ return 0;
+
+ inv_engine:
+ free (ti);
+ return trace_gpg_error (GPG_ERR_INV_ENGINE);
+}
+
+
/* We have read an entire key into tmp_key and should now finish it.
It is assumed that this releases tmp_key. */
static void
gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
enum
{
- RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
+ RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP,
RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
}
rectype = RT_NONE;
-#define NR_FIELDS 16
+#define NR_FIELDS 20
char *field[NR_FIELDS];
int fields = 0;
void *hook;
key = opd->tmp_key;
- TRACE2 (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx,
+ TRACE (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx,
"key = %p, line = %s", key, line ? line : "(null)");
if (!line)
rectype = RT_CRS;
else if (!strcmp (field[0], "fpr") && key)
rectype = RT_FPR;
+ else if (!strcmp (field[0], "grp") && key)
+ rectype = RT_GRP;
else if (!strcmp (field[0], "uid") && key)
rectype = RT_UID;
+ else if (!strcmp (field[0], "tfs") && key)
+ rectype = RT_TFS;
else if (!strcmp (field[0], "sub") && key)
rectype = RT_SUB;
else if (!strcmp (field[0], "ssb") && key)
else
rectype = RT_NONE;
- /* Only look at signatures immediately following a user ID. For
- this, clear the user ID pointer when encountering anything but a
- signature. */
- if (rectype != RT_SIG && rectype != RT_REV)
+ /* Only look at signature and trust info records immediately
+ following a user ID. For this, clear the user ID pointer when
+ encountering anything but a signature, trust record or subpacket. */
+ if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS &&
+ rectype != RT_SPK)
opd->tmp_uid = NULL;
/* Only look at subpackets immediately following a signature. For
{
int i = atoi (field[3]);
if (i >= 1 && i < 128)
- subkey->pubkey_algo = i;
+ subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
}
/* Field 5 has the long keyid. Allow short key IDs for the
set_mainkey_capability (key, field[11]);
/* Field 15 carries special flags of a secret key. */
- if (fields >= 15 && key->secret)
+ if (fields >= 15
+ && (key->secret
+ || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET)))
{
- err = parse_sec_field15 (subkey, field[14]);
+ err = parse_sec_field15 (key, subkey, field[14]);
if (err)
return err;
}
+
+ /* Field 17 has the curve name for ECC. */
+ if (fields >= 17 && *field[16])
+ {
+ subkey->curve = strdup (field[16]);
+ if (!subkey->curve)
+ return gpg_error_from_syserror ();
+ }
+
+ /* Field 18 has the compliance flags. */
+ if (fields >= 17 && *field[17])
+ PARSE_COMPLIANCE_FLAGS (field[17], subkey);
+
+ if (fields >= 20)
+ {
+ key->last_update = _gpgme_parse_timestamp_ul (field[18]);
+ key->origin = parse_keyorg (field[19]);
+ }
+
break;
case RT_SUB:
{
int i = atoi (field[3]);
if (i >= 1 && i < 128)
- subkey->pubkey_algo = i;
+ subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
}
/* Field 5 has the long keyid. */
set_subkey_capability (subkey, field[11]);
/* Field 15 carries special flags of a secret key. */
- if (fields >= 15 && key->secret)
+ if (fields >= 15
+ && (key->secret
+ || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET)))
{
- err = parse_sec_field15 (subkey, field[14]);
+ err = parse_sec_field15 (key, subkey, field[14]);
if (err)
return err;
}
+
+ /* Field 17 has the curve name for ECC. */
+ if (fields >= 17 && *field[16])
+ {
+ subkey->curve = strdup (field[16]);
+ if (!subkey->curve)
+ return gpg_error_from_syserror ();
+ }
+
+ /* Field 18 has the compliance flags. */
+ if (fields >= 17 && *field[17])
+ PARSE_COMPLIANCE_FLAGS (field[17], subkey);
+
break;
case RT_UID:
{
if (_gpgme_key_append_name (key, field[9], 1))
return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
- else
- {
- if (field[1])
- set_userid_flags (key, field[1]);
- opd->tmp_uid = key->_last_uid;
- }
+
+ if (field[1])
+ set_userid_flags (key, field[1]);
+ if (field[7] && *field[7])
+ {
+ gpgme_user_id_t uid = key->_last_uid;
+ assert (uid);
+ uid->uidhash = strdup (field[7]);
+ }
+ opd->tmp_uid = key->_last_uid;
+ if (fields >= 20)
+ {
+ opd->tmp_uid->last_update = _gpgme_parse_timestamp_ul (field[18]);
+ opd->tmp_uid->origin = parse_keyorg (field[19]);
+ }
}
break;
+ case RT_TFS:
+ if (opd->tmp_uid)
+ {
+ err = parse_tfs_record (opd->tmp_uid, field, fields);
+ if (err)
+ return err;
+ }
+ break;
+
case RT_FPR:
/* Field 10 has the fingerprint (take only the first one). */
if (fields >= 10 && field[9] && *field[9])
if (!subkey->fpr)
return gpg_error_from_syserror ();
}
+ /* If this is the first subkey, store the fingerprint also
+ in the KEY object. */
+ if (subkey == key->subkeys)
+ {
+ if (key->fpr && strcmp (key->fpr, subkey->fpr))
+ {
+ /* FPR already set but mismatch: Should never happen. */
+ return trace_gpg_error (GPG_ERR_INTERNAL);
+ }
+ if (!key->fpr)
+ {
+ key->fpr = strdup (subkey->fpr);
+ if (!key->fpr)
+ return gpg_error_from_syserror ();
+ }
+ }
}
/* Field 13 has the gpgsm chain ID (take only the first one). */
}
break;
+ case RT_GRP:
+ /* Field 10 has the keygrip. */
+ if (fields >= 10 && field[9] && *field[9])
+ {
+ /* Need to apply it to the last subkey because all subkeys
+ have a keygrip. */
+ subkey = key->_last_subkey;
+ if (!subkey->keygrip)
+ {
+ subkey->keygrip = strdup (field[9]);
+ if (!subkey->keygrip)
+ return gpg_error_from_syserror ();
+ }
+ }
+ break;
+
case RT_SIG:
case RT_REV:
if (!opd->tmp_uid)
{
int i = atoi (field[3]);
if (i >= 1 && i < 128)
- keysig->pubkey_algo = i;
+ keysig->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
}
/* Field 5 has the long keyid. */
gpgme_error_t err;
void *hook;
op_data_t opd;
+ int flags = 0;
- TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_start", ctx,
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_start", ctx,
"pattern=%s, secret_only=%i", pattern, secret_only);
if (!ctx)
if (err)
return TRACE_ERR (err);
+ if (ctx->offline)
+ flags |= GPGME_ENGINE_FLAG_OFFLINE;
+
err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
- ctx->keylist_mode);
+ ctx->keylist_mode, flags);
return TRACE_ERR (err);
}
gpgme_error_t err;
void *hook;
op_data_t opd;
+ int flags = 0;
- TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx,
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx,
"secret_only=%i, reserved=0x%x", secret_only, reserved);
if (!ctx)
if (err)
return TRACE_ERR (err);
+ if (ctx->offline)
+ flags |= GPGME_ENGINE_FLAG_OFFLINE;
+
err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
- reserved, ctx->keylist_mode);
+ reserved, ctx->keylist_mode,
+ flags);
+ return TRACE_ERR (err);
+}
+
+
+/* Start a keylist operation within CTX to show keys contained
+ * in DATA. */
+gpgme_error_t
+gpgme_op_keylist_from_data_start (gpgme_ctx_t ctx, gpgme_data_t data,
+ int reserved)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx, "");
+
+ if (!ctx || !data || reserved)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_reset (ctx, 2);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+
+ _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
+ err = _gpgme_engine_set_colon_line_handler (ctx->engine,
+ keylist_colon_handler, ctx);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_engine_op_keylist_data (ctx->engine, data);
return TRACE_ERR (err);
}
void *hook;
op_data_t opd;
- TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx);
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx, "");
if (!ctx || !r_key)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
return TRACE_ERR (err);
if (!opd->key_cond)
- return TRACE_ERR (gpg_error (GPG_ERR_EOF));
+ return TRACE_ERR (opd->keydb_search_err? opd->keydb_search_err
+ /**/ : gpg_error (GPG_ERR_EOF));
opd->key_cond = 0;
assert (opd->key_queue);
*r_key = queue_item->key;
free (queue_item);
- return TRACE_SUC2 ("key=%p (%s)", *r_key,
- ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
- (*r_key)->subkeys->fpr : "invalid");
+ TRACE_SUC ("key=%p (%s)", *r_key,
+ ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
+ (*r_key)->subkeys->fpr : "invalid");
+ return 0;
}
gpgme_error_t
gpgme_op_keylist_end (gpgme_ctx_t ctx)
{
- TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx);
+ TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx, "");
if (!ctx)
return gpg_error (GPG_ERR_INV_VALUE);
{
gpgme_ctx_t listctx;
gpgme_error_t err;
- gpgme_key_t key;
+ gpgme_key_t result, key;
- TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx,
+ TRACE_BEG (DEBUG_CTX, "gpgme_get_key", ctx,
"fpr=%s, secret=%i", fpr, secret);
+ if (r_key)
+ *r_key = NULL;
+
if (!ctx || !r_key || !fpr)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
err = gpgme_op_keylist_start (listctx, fpr, secret);
if (!err)
- err = gpgme_op_keylist_next (listctx, r_key);
+ err = gpgme_op_keylist_next (listctx, &result);
if (!err)
{
try_next_key:
else
{
if (!err
- && *r_key && (*r_key)->subkeys && (*r_key)->subkeys->fpr
+ && result && result->subkeys && result->subkeys->fpr
&& key && key->subkeys && key->subkeys->fpr
- && !strcmp ((*r_key)->subkeys->fpr, key->subkeys->fpr))
+ && !strcmp (result->subkeys->fpr, key->subkeys->fpr))
{
/* The fingerprint is identical. We assume that this is
the same key and don't mark it as an ambiguous. This
gpgme_key_unref (key);
err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
}
- gpgme_key_unref (*r_key);
+ gpgme_key_unref (result);
+ result = NULL;
}
}
gpgme_release (listctx);
if (! err)
{
- TRACE_LOG2 ("key=%p (%s)", *r_key,
+ *r_key = result;
+ TRACE_LOG ("key=%p (%s)", *r_key,
((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
(*r_key)->subkeys->fpr : "invalid");
}