gpg: Fix detection of the AEAD feature flag.
[gnupg.git] / g10 / getkey.c
index 74eed13..fe64835 100644 (file)
@@ -144,7 +144,7 @@ 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,
-                               unsigned int *r_flags);
+                               int want_secret, unsigned int *r_flags);
 static void print_status_key_considered (kbnode_t keyblock, unsigned int flags);
 
 
@@ -413,34 +413,35 @@ pubkeys_free (pubkey_t keys)
     }
 }
 
-/* Returns all keys that match the search specification SEARCH_TERMS.
-
-   This function also checks for and warns about duplicate entries in
-   the keydb, which can occur if the user has configured multiple
-   keyrings or keyboxes or if a keyring or keybox was corrupted.
-
-   Note: SEARCH_TERMS will not be expanded (i.e., it may not be a
-   group).
-
-   USE is the operation for which the key is required.  It must be
-   either PUBKEY_USAGE_ENC, PUBKEY_USAGE_SIG, PUBKEY_USAGE_CERT or
-   PUBKEY_USAGE_AUTH.
-
-   XXX: Currently, only PUBKEY_USAGE_ENC and PUBKEY_USAGE_SIG are
-   implemented.
-
-   INCLUDE_UNUSABLE indicates whether disabled keys are allowed.
-   (Recipients specified with --encrypt-to and --hidden-encrypt-to may
-   be disabled.  It is possible to edit disabled keys.)
-
-   SOURCE is the context in which SEARCH_TERMS was specified, e.g.,
-   "--encrypt-to", etc.  If this function is called interactively,
-   then this should be NULL.
-
-   If WARN_POSSIBLY_AMBIGUOUS is set, then emits a warning if the user
-   does not specify a long key id or a fingerprint.
 
-   The results are placed in *KEYS.  *KEYS must be NULL!  */
+/* Returns all keys that match the search specification SEARCH_TERMS.
+ *
+ * This function also checks for and warns about duplicate entries in
+ * the keydb, which can occur if the user has configured multiple
+ * keyrings or keyboxes or if a keyring or keybox was corrupted.
+ *
+ * Note: SEARCH_TERMS will not be expanded (i.e., it may not be a
+ * group).
+ *
+ * USE is the operation for which the key is required.  It must be
+ * either PUBKEY_USAGE_ENC, PUBKEY_USAGE_SIG, PUBKEY_USAGE_CERT or
+ * PUBKEY_USAGE_AUTH.
+ *
+ * INCLUDE_UNUSABLE indicates whether disabled keys are allowed.
+ * (Recipients specified with --encrypt-to and --hidden-encrypt-to may
+ * be disabled.  It is possible to edit disabled keys.)
+ *
+ * SOURCE is the context in which SEARCH_TERMS was specified, e.g.,
+ * "--encrypt-to", etc.  If this function is called interactively,
+ * then this should be NULL.
+ *
+ * If WARN_POSSIBLY_AMBIGUOUS is set, then emits a warning if the user
+ * does not specify a long key id or a fingerprint.
+ *
+ * The results are placed in *KEYS.  *KEYS must be NULL!
+ *
+ * Fixme: Currently, only PUBKEY_USAGE_ENC and PUBKEY_USAGE_SIG are
+ * implemented.  */
 gpg_error_t
 get_pubkeys (ctrl_t ctrl,
              char *search_terms, int use, int include_unusable, char *source,
@@ -448,30 +449,23 @@ get_pubkeys (ctrl_t ctrl,
              pubkey_t *r_keys)
 {
   /* We show a warning when a key appears multiple times in the DB.
-     This can happen for two reasons:
-
-       - The user has configured multiple keyrings or keyboxes.
-
-       - The keyring or keybox has been corrupted in some way, e.g., a
-         bug or a random process changing them.
-
-     For each duplicate, we only want to show the key once.  Hence,
-     this list.  */
+   * This can happen for two reasons:
+   *
+   *   - The user has configured multiple keyrings or keyboxes.
+   *
+   *   - The keyring or keybox has been corrupted in some way, e.g., a
+   *     bug or a random process changing them.
+   *
+   * For each duplicate, we only want to show the key once.  Hence,
+   * this list.  */
   static strlist_t key_dups;
-
-  /* USE transformed to a string.  */
-  char *use_str;
-
   gpg_error_t err;
-
+  char *use_str;   /* USE transformed to a string.  */
   KEYDB_SEARCH_DESC desc;
-
   GETKEY_CTX ctx;
   pubkey_t results = NULL;
   pubkey_t r;
-
   int count;
-
   char fingerprint[2 * MAX_FINGERPRINT_LEN + 1];
 
   if (DBG_LOOKUP)
@@ -503,7 +497,7 @@ get_pubkeys (ctrl_t ctrl,
                 search_terms, gpg_strerror (err));
       if (!opt.quiet && source)
         log_info (_("(check argument of option '%s')\n"), source);
-      goto out;
+      goto leave;
     }
 
   if (warn_possibly_ambiguous
@@ -523,8 +517,16 @@ get_pubkeys (ctrl_t ctrl,
   count = 0;
   do
     {
-      PKT_public_key *pk = xmalloc_clear (sizeof *pk);
+      PKT_public_key *pk;
       KBNODE kb;
+
+      pk = xtrycalloc (1, sizeof *pk);
+      if (!pk)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+
       pk->req_usage = use;
 
       if (! ctx)
@@ -533,17 +535,14 @@ get_pubkeys (ctrl_t ctrl,
       else
         err = getkey_next (ctrl, ctx, pk, &kb);
 
-      if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
-        /* No more results.   */
+      if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) /* No more results.   */
         {
           xfree (pk);
           break;
         }
-      else if (err)
-        /* An error (other than "not found").  */
+      else if (err) /* An error (other than "not found").  */
         {
-          log_error (_("error looking up: %s\n"),
-                     gpg_strerror (err));
+          log_error (_("error looking up: %s\n"), gpg_strerror (err));
           xfree (pk);
           break;
         }
@@ -551,7 +550,13 @@ get_pubkeys (ctrl_t ctrl,
       /* Another result!  */
       count ++;
 
-      r = xmalloc_clear (sizeof (*r));
+      r = xtrycalloc (1, sizeof (*r));
+      if (!r)
+        {
+          err = gpg_error_from_syserror ();
+          xfree (pk);
+          goto leave;
+        }
       r->pk = pk;
       r->keyblock = kb;
       r->next = results;
@@ -570,8 +575,7 @@ get_pubkeys (ctrl_t ctrl,
     }
 
   if (! results && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
-    /* No match.  */
-    {
+    { /* No match.  */
       if (DBG_LOOKUP)
         log_debug ("%s: '%s' not found.\n", __func__, search_terms);
 
@@ -579,15 +583,15 @@ get_pubkeys (ctrl_t ctrl,
       if (!opt.quiet && source)
         log_info (_("(check argument of option '%s')\n"), source);
 
-      goto out;
+      goto leave;
     }
   else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
-    /* No more matches.  */
-    ;
+    ; /* No more matches.  */
   else if (err)
-    /* Some other error.  An error message was already printed
-       out.  Free RESULTS and continue.  */
-    goto out;
+    { /* Some other error.  An error message was already printed out.
+       * Free RESULTS and continue.  */
+      goto leave;
+    }
 
   /* Check for duplicates.  */
   if (DBG_LOOKUP)
@@ -607,8 +611,7 @@ get_pubkeys (ctrl_t ctrl,
         {
           if (cmp_public_keys (r->keyblock->pkt->pkt.public_key,
                                r2->keyblock->pkt->pkt.public_key) != 0)
-            /* Not a dup.  */
-            {
+            { /* Not a dup.  */
               prevp = &r2->next;
               next = r2->next;
               continue;
@@ -652,7 +655,7 @@ get_pubkeys (ctrl_t ctrl,
                                    fingerprint, sizeof fingerprint));
     }
 
out:
leave:
   if (err)
     pubkeys_free (results);
   else
@@ -723,8 +726,13 @@ get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid)
   /* More init stuff.  */
   if (!pk)
     {
-      pk = xmalloc_clear (sizeof *pk);
       internal++;
+      pk = xtrycalloc (1, sizeof *pk);
+      if (!pk)
+        {
+          rc = gpg_error_from_syserror ();
+          goto leave;
+        }
     }
 
 
@@ -1448,8 +1456,9 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
          if (!rc)
            {
              /* Key found.  */
-             log_info (_("automatically retrieved '%s' via %s\n"),
-                       name, mechanism);
+              if (opt.verbose)
+                log_info (_("automatically retrieved '%s' via %s\n"),
+                          name, mechanism);
              break;
            }
          if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY
@@ -1583,31 +1592,30 @@ pubkey_cmp (ctrl_t ctrl, const char *name, struct pubkey_cmp_cookie *old,
 /* This function works like get_pubkey_byname, but if the name
  * resembles a mail address, the results are ranked and only the best
  * result is returned.  */
-int
+gpg_error_t
 get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
                         const char *name, KBNODE *ret_keyblock,
                         int include_unusable, int no_akl)
 {
-  int rc;
+  gpg_error_t err;
   struct getkey_ctx_s *ctx = NULL;
 
   if (retctx)
     *retctx = NULL;
 
-  rc = get_pubkey_byname (ctrl, &ctx, pk, name, ret_keyblock,
-                          NULL, include_unusable, no_akl);
-  if (rc)
+  err = get_pubkey_byname (ctrl, &ctx, pk, name, ret_keyblock,
+                           NULL, include_unusable, no_akl);
+  if (err)
     {
-      if (ctx)
-        getkey_end (ctrl, ctx);
-      return rc;
+      getkey_end (ctrl, ctx);
+      return err;
     }
 
   if (is_valid_mailbox (name) && ctx)
     {
       /* Rank results and return only the most relevant key.  */
       struct pubkey_cmp_cookie best = { 0 };
-      struct pubkey_cmp_cookie new;
+      struct pubkey_cmp_cookie new = { 0 };
       kbnode_t new_keyblock;
 
       while (getkey_next (ctrl, ctx, &new.key, &new_keyblock) == 0)
@@ -1647,16 +1655,17 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
             {
               ctx = xtrycalloc (1, sizeof **retctx);
               if (! ctx)
-                rc = gpg_error_from_syserror ();
+                err = gpg_error_from_syserror ();
               else
                 {
                   ctx->kr_handle = keydb_new ();
                   if (! ctx->kr_handle)
                     {
+                      err = gpg_error_from_syserror ();
                       xfree (ctx);
+                      ctx = NULL;
                       if (retctx)
                         *retctx = NULL;
-                      rc = gpg_error_from_syserror ();
                     }
                   else
                     {
@@ -1671,7 +1680,7 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
                         {
                           release_kbnode (*ret_keyblock);
                           *ret_keyblock = NULL;
-                          rc = getkey_next (ctrl, ctx, NULL, ret_keyblock);
+                          err = getkey_next (ctrl, ctx, NULL, ret_keyblock);
                         }
                     }
                 }
@@ -1684,7 +1693,7 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
         }
     }
 
-  if (rc && ctx)
+  if (err && ctx)
     {
       getkey_end (ctrl, ctx);
       ctx = NULL;
@@ -1695,7 +1704,7 @@ get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
   else
     getkey_end (ctrl, ctx);
 
-  return rc;
+  return err;
 }
 
 \f
@@ -1734,7 +1743,7 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname)
       /* 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, &infoflags);
+      found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, &infoflags);
       print_status_key_considered (keyblock, infoflags);
       if (found_key)
         pk_from_block (pk, keyblock, found_key);
@@ -1801,6 +1810,8 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
       ctx.items[0].mode = fprint_len == 16 ? KEYDB_SEARCH_MODE_FPR16
        : KEYDB_SEARCH_MODE_FPR20;
       memcpy (ctx.items[0].u.fpr, fprint, fprint_len);
+      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);
@@ -1828,16 +1839,47 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock,
  *
  * Like get_pubkey_byfprint, PK may be NULL.  In that case, this
  * function effectively just checks for the existence of the key.  */
-int
+gpg_error_t
 get_pubkey_byfprint_fast (PKT_public_key * pk,
                          const byte * fprint, size_t fprint_len)
 {
-  int rc = 0;
-  KEYDB_HANDLE hd;
+  gpg_error_t err;
   KBNODE keyblock;
+
+  err = get_keyblock_byfprint_fast (&keyblock, NULL, fprint, fprint_len, 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_byfprint_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 to do an insert
+ * operation on a locked keydb handle.  */
+gpg_error_t
+get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
+                            const byte *fprint, size_t fprint_len, int lock)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE hd;
+  kbnode_t keyblock;
   byte fprbuf[MAX_FINGERPRINT_LEN];
   int i;
 
+  if (r_keyblock)
+    *r_keyblock = NULL;
+  if (r_hd)
+    *r_hd = NULL;
+
   for (i = 0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++)
     fprbuf[i] = fprint[i];
   while (i < MAX_FINGERPRINT_LEN)
@@ -1847,32 +1889,58 @@ get_pubkey_byfprint_fast (PKT_public_key * pk,
   if (!hd)
     return gpg_error_from_syserror ();
 
-  rc = keydb_search_fpr (hd, fprbuf);
-  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+  if (lock)
     {
-      keydb_release (hd);
-      return GPG_ERR_NO_PUBKEY;
+      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);
     }
-  rc = keydb_get_keyblock (hd, &keyblock);
-  keydb_release (hd);
-  if (rc)
+
+  /* Fo all other errors we return the handle.  */
+  if (r_hd)
+    *r_hd = hd;
+
+  err = keydb_search_fpr (hd, fprbuf);
+  if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
     {
-      log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
-      return GPG_ERR_NO_PUBKEY;
+      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 (pk)
-    copy_public_key (pk, keyblock->pkt->pkt.public_key);
-  release_kbnode (keyblock);
 
   /* Not caching key here since it won't have all of the fields
      properly set. */
 
+  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)
 {
@@ -2377,8 +2445,8 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated)
 {
   PKT_user_id *uid = uidnode->pkt->pkt.user_id;
   PKT_signature *sig = signode->pkt->pkt.signature;
-  const byte *p, *sym, *hash, *zip;
-  size_t n, nsym, nhash, nzip;
+  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. */
@@ -2433,6 +2501,9 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated)
   p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM, &n);
   sym = p;
   nsym = p ? n : 0;
+  p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD, &n);
+  aead = p;
+  naead = p ? n : 0;
   p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH, &n);
   hash = p;
   nhash = p ? n : 0;
@@ -2441,7 +2512,7 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated)
   nzip = p ? n : 0;
   if (uid->prefs)
     xfree (uid->prefs);
-  n = nsym + nhash + nzip;
+  n = nsym + naead + nhash + nzip;
   if (!n)
     uid->prefs = NULL;
   else
@@ -2453,6 +2524,11 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated)
          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;
@@ -2473,6 +2549,12 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated)
   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->hashed, 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->hashed, SIGSUBPKT_KS_FLAGS, &n);
@@ -3291,6 +3373,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
   PKT_public_key *main_pk;
   prefitem_t *prefs;
   unsigned int mdc_feature;
+  unsigned int aead_feature;
 
   if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY)
     {
@@ -3352,7 +3435,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
    * all preferences.
    * Do a similar thing for the MDC feature flag.  */
   prefs = NULL;
-  mdc_feature = 0;
+  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
@@ -3361,6 +3444,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
        {
          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;
        }
     }
@@ -3374,6 +3458,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
            xfree (pk->prefs);
          pk->prefs = copy_prefs (prefs);
          pk->flags.mdc = mdc_feature;
+         pk->flags.aead = aead_feature;
        }
     }
 }
@@ -3428,7 +3513,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
  */
 static kbnode_t
 finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
-               unsigned int *r_flags)
+               int want_secret, unsigned int *r_flags)
 {
   kbnode_t k;
 
@@ -3570,6 +3655,13 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
              continue;
            }
 
+          if (want_secret && agent_probe_secret_key (NULL, pk))
+            {
+              if (DBG_LOOKUP)
+                log_debug ("\tno secret key\n");
+              continue;
+            }
+
          if (DBG_LOOKUP)
            log_debug ("\tsubkey might be fine\n");
          /* In case a key has a timestamp of 0 set, we make sure
@@ -3757,7 +3849,7 @@ lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
        * merge_selfsigs.  */
       merge_selfsigs (ctrl, keyblock);
       found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact,
-                                 &infoflags);
+                                 want_secret, &infoflags);
       print_status_key_considered (keyblock, infoflags);
       if (found_key)
        {
@@ -4044,15 +4136,20 @@ get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk,
  *********************************************/
 
 /* Return a string with a printable representation of the user_id.
- * this string must be freed by xfree.   */
+ * 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, size_t *r_len)
+get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len,
+                    int *r_nouid)
 {
   user_id_db_t r;
   keyid_list_t a;
   int pass = 0;
   char *p;
 
+  if (r_nouid)
+    *r_nouid = 0;
+
   /* Try it two times; second pass reads from the database.  */
   do
     {
@@ -4099,6 +4196,8 @@ get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len)
   else
     p = xasprintf ("%s [?]", keystr (keyid));
 
+  if (r_nouid)
+    *r_nouid = 1;
   if (r_len)
     *r_len = strlen (p);
   return p;
@@ -4108,7 +4207,7 @@ get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len)
 char *
 get_user_id_string_native (ctrl_t ctrl, u32 * keyid)
 {
-  char *p = get_user_id_string (ctrl, keyid, 0, NULL);
+  char *p = get_user_id_string (ctrl, keyid, 0, NULL, NULL);
   char *p2 = utf8_to_native (p, strlen (p), 0);
   xfree (p);
   return p2;
@@ -4118,15 +4217,15 @@ get_user_id_string_native (ctrl_t ctrl, u32 * keyid)
 char *
 get_long_user_id_string (ctrl_t ctrl, u32 * keyid)
 {
-  return get_user_id_string (ctrl, keyid, 1, NULL);
+  return get_user_id_string (ctrl, keyid, 1, NULL, NULL);
 }
 
 
 /* Please try to use get_user_byfpr instead of this one.  */
 char *
-get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn)
+get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid)
 {
-  return get_user_id_string (ctrl, keyid, 2, rn);
+  return get_user_id_string (ctrl, keyid, 2, rn, r_nouid);
 }
 
 
@@ -4135,7 +4234,7 @@ char *
 get_user_id_native (ctrl_t ctrl, u32 *keyid)
 {
   size_t rn;
-  char *p = get_user_id (ctrl, keyid, &rn);
+  char *p = get_user_id (ctrl, keyid, &rn, NULL);
   char *p2 = utf8_to_native (p, rn, 0);
   xfree (p);
   return p2;
@@ -4229,10 +4328,12 @@ release_akl (void)
 
 /* Returns false on error. */
 int
-parse_auto_key_locate (char *options)
+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;
@@ -4271,6 +4372,7 @@ parse_auto_key_locate (char *options)
       else
        {
          free_akl (akl);
+          xfree (options_buf);
          return 0;
        }
 
@@ -4299,6 +4401,7 @@ parse_auto_key_locate (char *options)
        }
     }
 
+  xfree (options_buf);
   return 1;
 }
 
@@ -4325,6 +4428,11 @@ int
 parse_key_origin (char *string)
 {
   int i;
+  char *comma;
+
+  comma = strchr (string, ',');
+  if (comma)
+    *comma = 0;
 
   if (!ascii_strcasecmp (string, "help"))
     {
@@ -4338,9 +4446,19 @@ parse_key_origin (char *string)
     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;
 }