gpg: Minor cleanup for key listing related code.
[gnupg.git] / g10 / keyserver.c
index 28b4a10..1b2e128 100644 (file)
@@ -1,6 +1,7 @@
 /* keyserver.c - generic keyserver code
  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
  *               2009, 2011, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -110,7 +111,8 @@ static struct parse_options keyserver_opts[]=
 
 static gpg_error_t keyserver_get (ctrl_t ctrl,
                                   KEYDB_SEARCH_DESC *desc, int ndesc,
-                                  struct keyserver_spec *keyserver);
+                                  struct keyserver_spec *keyserver,
+                                  unsigned char **r_fpr, size_t *r_fprlen);
 static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
                                   struct keyserver_spec *keyserver);
 
@@ -819,7 +821,7 @@ show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc,
           }
         for (idx = 0; idx < numidx; idx++)
           selarray[idx] = desc[numarray[idx]-1];
-        err = keyserver_get (ctrl, selarray, numidx, NULL);
+        err = keyserver_get (ctrl, selarray, numidx, NULL, NULL, NULL);
         xfree (selarray);
       }
     }
@@ -831,17 +833,29 @@ show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc,
 
 
 /* This is a callback used by call-dirmngr.c to process the result of
-   KS_SEARCH command.  LINE is the actual data line received with all
-   escaping removed and guaranteed to be exactly one line with
-   stripped LF; an EOF is indicated by LINE passed as NULL.  LINE may
-   be modified after return.  */
+   KS_SEARCH command.  If SPECIAL is 0, LINE is the actual data line
+   received with all escaping removed and guaranteed to be exactly one
+   line with stripped LF; an EOF is indicated by LINE passed as NULL.
+   If special is 1, the line contains the source of the information
+   (usually an URL).  LINE may be modified after return.  */
 static gpg_error_t
-search_line_handler (void *opaque, char *line)
+search_line_handler (void *opaque, int special, char *line)
 {
   struct search_line_handler_parm_s *parm = opaque;
   gpg_error_t err = 0;
   struct keyrec *keyrec;
 
+  if (special == 1)
+    {
+      log_info ("data source: %s\n", line);
+      return 0;
+    }
+  else if (special)
+    {
+      log_debug ("unknown value %d for special search callback", special);
+      return 0;
+    }
+
   if (parm->eof_seen && line)
     {
       log_debug ("ooops: unexpected data after EOF\n");
@@ -851,7 +865,7 @@ search_line_handler (void *opaque, char *line)
   /* Print the received line.  */
   if (opt.with_colons && line)
     {
-      log_debug ("%s\n",line);
+      es_printf ("%s\n", line);
     }
 
   /* Look for an info: line.  The only current info: values defined
@@ -1027,6 +1041,86 @@ keyserver_export (ctrl_t ctrl, strlist_t users)
   return rc;
 }
 
+
+/* Structure to convey the arg to keyserver_retrieval_screener.  */
+struct ks_retrieval_screener_arg_s
+{
+  KEYDB_SEARCH_DESC *desc;
+  int ndesc;
+};
+
+
+/* Check whether a key matches the search description.  The function
+   returns 0 if the key shall be imported.  */
+static gpg_error_t
+keyserver_retrieval_screener (kbnode_t keyblock, void *opaque)
+{
+  struct ks_retrieval_screener_arg_s *arg = opaque;
+  KEYDB_SEARCH_DESC *desc = arg->desc;
+  int ndesc = arg->ndesc;
+  kbnode_t node;
+  PKT_public_key *pk;
+  int n;
+  u32 keyid[2];
+  byte fpr[MAX_FINGERPRINT_LEN];
+  size_t fpr_len = 0;
+
+  /* Secret keys are not expected from a keyserver.  We do not
+     care about secret subkeys because the import code takes care
+     of skipping them.  Not allowing an import of a public key
+     with a secret subkey would make it too easy to inhibit the
+     downloading of a public key.  Recall that keyservers do only
+     limited checks.  */
+  node = find_kbnode (keyblock, PKT_SECRET_KEY);
+  if (node)
+    return gpg_error (GPG_ERR_GENERAL);   /* Do not import. */
+
+  if (!ndesc)
+    return 0; /* Okay if no description given.  */
+
+  /* Loop over all key packets.  */
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype != PKT_PUBLIC_KEY
+          && node->pkt->pkttype != PKT_PUBLIC_SUBKEY)
+        continue;
+
+      pk = node->pkt->pkt.public_key;
+      fingerprint_from_pk (pk, fpr, &fpr_len);
+      keyid_from_pk (pk, keyid);
+
+      /* Compare requested and returned fingerprints if available. */
+      for (n = 0; n < ndesc; n++)
+        {
+          if (desc[n].mode == KEYDB_SEARCH_MODE_FPR20)
+            {
+              if (fpr_len == 20 && !memcmp (fpr, desc[n].u.fpr, 20))
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_FPR16)
+            {
+              if (fpr_len == 16 && !memcmp (fpr, desc[n].u.fpr, 16))
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID)
+            {
+              if (keyid[0] == desc[n].u.kid[0] && keyid[1] == desc[n].u.kid[1])
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+            {
+              if (keyid[1] == desc[n].u.kid[1])
+                return 0;
+            }
+          else /* No keyid or fingerprint - can't check.  */
+            return 0; /* allow import.  */
+        }
+    }
+
+  return gpg_error (GPG_ERR_GENERAL);
+}
+
+
 int
 keyserver_import (ctrl_t ctrl, strlist_t users)
 {
@@ -1059,13 +1153,31 @@ keyserver_import (ctrl_t ctrl, strlist_t users)
     }
 
   if(count>0)
-    rc=keyserver_get (ctrl, desc, count, NULL);
+    rc=keyserver_get (ctrl, desc, count, NULL, NULL, NULL);
 
   xfree(desc);
 
   return rc;
 }
 
+
+/* Import all keys that exactly match NAME */
+int
+keyserver_import_name (ctrl_t ctrl, const char *name,
+                       unsigned char **fpr, size_t *fprlen,
+                       struct keyserver_spec *keyserver)
+{
+  KEYDB_SEARCH_DESC desc;
+
+  memset (&desc, 0, sizeof desc);
+
+  desc.mode = KEYDB_SEARCH_MODE_EXACT;
+  desc.u.name = name;
+
+  return keyserver_get (ctrl, &desc, 1, keyserver, fpr, fprlen);
+}
+
+
 int
 keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
                         struct keyserver_spec *keyserver)
@@ -1085,7 +1197,7 @@ keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
 
   /* TODO: Warn here if the fingerprint we got doesn't match the one
      we asked for? */
-  return keyserver_get (ctrl, &desc, 1, keyserver);
+  return keyserver_get (ctrl, &desc, 1, keyserver, NULL, NULL);
 }
 
 int
@@ -1100,7 +1212,7 @@ keyserver_import_keyid (ctrl_t ctrl,
   desc.u.kid[0]=keyid[0];
   desc.u.kid[1]=keyid[1];
 
-  return keyserver_get (ctrl, &desc,1, keyserver);
+  return keyserver_get (ctrl, &desc,1, keyserver, NULL, NULL);
 }
 
 /* code mostly stolen from do_export_stream */
@@ -1304,7 +1416,7 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
              /* We use the keyserver structure we parsed out before.
                 Note that a preferred keyserver without a scheme://
                 will be interpreted as hkp:// */
-             rc = keyserver_get (ctrl, &desc[i], 1, keyserver);
+             rc = keyserver_get (ctrl, &desc[i], 1, keyserver, NULL, NULL);
              if(rc)
                log_info(_("WARNING: unable to refresh key %s"
                           " via %s: %s\n"),keystr_from_desc(&desc[i]),
@@ -1334,7 +1446,7 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
                     count,opt.keyserver->uri);
        }
 
-      rc=keyserver_get (ctrl, desc, numdesc, NULL);
+      rc=keyserver_get (ctrl, desc, numdesc, NULL, NULL, NULL);
     }
 
   xfree(desc);
@@ -1457,27 +1569,22 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
 
 
 
-/* Called using:
-
-import_name:
-  rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
-                       0, fpr, fpr_len, keyserver);
-
-import_ldap:
-  rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
-                       0, fpr, fpr_len, keyserver);
-
- */
-
+/* Retrieve a key from a keyserver.  The search pattern are in
+   (DESC,NDESC).  Allowed search modes are keyid, fingerprint, and
+   exact searches.  KEYSERVER gives an optional override keyserver. If
+   (R_FPR,R_FPRLEN) are not NULL, the may retrun the fingerprint of
+   one imported key.  */
 static gpg_error_t
 keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
-               struct keyserver_spec *keyserver)
+               struct keyserver_spec *keyserver,
+               unsigned char **r_fpr, size_t *r_fprlen)
 
 {
   gpg_error_t err = 0;
   char **pattern;
   int idx, npat;
   estream_t datastream;
+  char *source = NULL;
 
   /* Create an array filled with a search pattern for each key.  The
      array is delimited by a NULL entry.  */
@@ -1523,10 +1630,12 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
         }
       else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT)
         {
-          /* FIXME: We don't need this.  It is used as a dummy by
-             keyserver_fetch which passes an entire URL.  Better use a
-             separate function here. */
-          pattern[npat] = xtrystrdup ("0x0000000000000000");
+          /* The Dirmngr uses also classify_user_id to detect the type
+             of the search string.  By adding the '=' prefix we force
+             Dirmngr's KS_GET to consider this an exact search string.
+             (In gpg 1.4 and gpg 2.0 the keyserver helpers used the
+             KS_GETNAME command to indicate this.)  */
+          pattern[npat] = strconcat ("=", desc[idx].u.name, NULL);
           if (!pattern[npat])
             err = gpg_error_from_syserror ();
           else
@@ -1561,13 +1670,17 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
     }
 
 
-  err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream);
+  err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream, &source);
   for (idx=0; idx < npat; idx++)
     xfree (pattern[idx]);
   xfree (pattern);
+  if (opt.verbose && source)
+    log_info ("data source: %s\n", source);
+
   if (!err)
     {
       void *stats_handle;
+      struct ks_retrieval_screener_arg_s screenerarg;
 
       stats_handle = import_new_stats_handle();
 
@@ -1583,14 +1696,19 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
          never accept or send them but we better protect against rogue
          keyservers. */
 
-      import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
+      screenerarg.desc = desc;
+      screenerarg.ndesc = ndesc;
+      import_keys_es_stream (ctrl, datastream, stats_handle,
+                             r_fpr, r_fprlen,
                              (opt.keyserver_options.import_options
-                              | IMPORT_NO_SECKEY));
+                              | IMPORT_NO_SECKEY),
+                             keyserver_retrieval_screener, &screenerarg);
+
       import_print_stats (stats_handle);
       import_release_stats_handle (stats_handle);
     }
   es_fclose (datastream);
-
+  xfree (source);
 
   return err;
 }
@@ -1650,13 +1768,16 @@ keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
 }
 
 
+/* Loop over all URLs in STRLIST and fetch the key at that URL.  Note
+   that the fetch operation ignores the configured key servers and
+   instead directly retrieves the keys.  */
 int
 keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
 {
   gpg_error_t err;
   strlist_t sl;
   estream_t datastream;
-  unsigned int options = opt.keyserver_options.import_options;
+  unsigned int save_options = opt.keyserver_options.import_options;
 
   /* Switch on fast-import, since fetch can handle more than one
      import and we don't want each set to rebuild the trustdb.
@@ -1675,7 +1796,8 @@ keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
 
           stats_handle = import_new_stats_handle();
           import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
-                                 opt.keyserver_options.import_options);
+                                 opt.keyserver_options.import_options,
+                                 NULL, NULL);
 
           import_print_stats (stats_handle);
           import_release_stats_handle (stats_handle);
@@ -1686,7 +1808,7 @@ keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
       es_fclose (datastream);
     }
 
-  opt.keyserver_options.import_options = options;
+  opt.keyserver_options.import_options = save_options;
 
   /* If the original options didn't have fast import, and the trustdb
      is dirty, rebuild. */
@@ -1725,7 +1847,8 @@ keyserver_import_cert (ctrl_t ctrl,
 
       err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len,
                                    (opt.keyserver_options.import_options
-                                    | IMPORT_NO_SECKEY));
+                                    | IMPORT_NO_SECKEY),
+                                   NULL, NULL);
 
       opt.no_armor=armor_status;
 
@@ -1805,31 +1928,18 @@ keyserver_import_pka (ctrl_t ctrl,
   return rc;
 }
 
-/* Import all keys that match name */
-int
-keyserver_import_name (ctrl_t ctrl, const char *name,
-                       unsigned char **fpr, size_t *fpr_len,
-                       struct keyserver_spec *keyserver)
-{
-  strlist_t list=NULL;
-  int rc;
-
-  append_to_strlist(&list,name);
-
-  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* FIXME */
-       /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
-       /*                 0, fpr, fpr_len, keyserver); */
-
-  free_strlist(list);
-
-  return rc;
-}
 
 /* Import a key by name using LDAP */
 int
 keyserver_import_ldap (ctrl_t ctrl,
-                       const char *name,unsigned char **fpr,size_t *fpr_len)
+                       const char *name, unsigned char **fpr, size_t *fprlen)
 {
+  (void)ctrl;
+  (void)name;
+  (void)fpr;
+  (void)fprlen;
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
+#if 0
   char *domain;
   struct keyserver_spec *keyserver;
   strlist_t list=NULL;
@@ -1900,4 +2010,5 @@ keyserver_import_ldap (ctrl_t ctrl,
   free_keyserver_spec(keyserver);
 
   return rc;
+#endif
 }