Imported Upstream version 1.14.0
[platform/upstream/gpgme.git] / src / keylist.c
index 465b472..a4de3ad 100644 (file)
@@ -1,22 +1,23 @@
 /* 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
@@ -33,6 +34,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 
 /* Suppress warning for accessing deprecated member "class".  */
 #define _GPGME_IN_GPGME
@@ -53,6 +55,9 @@ typedef struct
 {
   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.  */
@@ -96,19 +101,19 @@ gpgme_op_keylist_result (gpgme_ctx_t ctx)
   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;
 }
 
@@ -121,6 +126,8 @@ keylist_status_handler (void *priv, gpgme_status_code_t code, char *args)
   void *hook;
   op_data_t opd;
 
+  (void)args;
+
   err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
   opd = hook;
   if (err)
@@ -132,10 +139,17 @@ keylist_status_handler (void *priv, gpgme_status_code_t code, char *args)
       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
@@ -363,11 +377,30 @@ set_ownertrust (gpgme_key_t key, const char *src)
 }
 
 
+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.  */
@@ -375,17 +408,25 @@ parse_sec_field15 (gpgme_subkey_t subkey, char *field)
     {
       /* 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.  */
@@ -395,6 +436,98 @@ parse_sec_field15 (gpgme_subkey_t subkey, char *field)
 }
 
 
+/* 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
@@ -418,11 +551,11 @@ keylist_colon_handler (void *priv, char *line)
   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;
@@ -439,7 +572,7 @@ keylist_colon_handler (void *priv, char *line)
 
   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)
@@ -471,8 +604,12 @@ keylist_colon_handler (void *priv, char *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)
@@ -482,10 +619,11 @@ keylist_colon_handler (void *priv, char *line)
   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
@@ -537,7 +675,7 @@ keylist_colon_handler (void *priv, char *line)
        {
          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
@@ -578,12 +716,33 @@ keylist_colon_handler (void *priv, char *line)
        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:
@@ -614,7 +773,7 @@ keylist_colon_handler (void *priv, char *line)
        {
          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.  */
@@ -640,12 +799,27 @@ keylist_colon_handler (void *priv, char *line)
        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:
@@ -654,15 +828,33 @@ keylist_colon_handler (void *priv, char *line)
        {
          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])
@@ -676,6 +868,22 @@ keylist_colon_handler (void *priv, char *line)
               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).  */
@@ -687,6 +895,22 @@ keylist_colon_handler (void *priv, char *line)
        }
       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)
@@ -728,7 +952,7 @@ keylist_colon_handler (void *priv, char *line)
        {
          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.  */
@@ -859,8 +1083,9 @@ gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
   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)
@@ -883,8 +1108,11 @@ gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
   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);
 }
 
@@ -899,8 +1127,9 @@ gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
   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)
@@ -922,8 +1151,48 @@ gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
   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);
 }
 
@@ -937,7 +1206,7 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
   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));
@@ -959,7 +1228,8 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
        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);
@@ -972,9 +1242,10 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
   *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;
 }
 
 
@@ -982,7 +1253,7 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
 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);
@@ -999,11 +1270,14 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
 {
   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));
 
@@ -1033,7 +1307,7 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
 
   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:
@@ -1043,9 +1317,9 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_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
@@ -1061,13 +1335,15 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
              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");
     }