gpg: Minor cleanup for key listing related code.
[gnupg.git] / g10 / keyserver.c
index 5cc7438..1b2e128 100644 (file)
@@ -1,6 +1,7 @@
 /* keyserver.c - generic keyserver code
  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- *               2009, 2011 Free Software Foundation, Inc.
+ *               2009, 2011, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -110,13 +111,15 @@ 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);
 
 
-/* Reasonable guess */
-#define DEFAULT_MAX_CERT_SIZE 16384
+/* Reasonable guess.  The commonly used test key simon.josefsson.org
+   is larger than 32k, thus we need at least this value. */
+#define DEFAULT_MAX_CERT_SIZE 65536
 
 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE;
 
@@ -172,7 +175,7 @@ parse_keyserver_options(char *options)
 #ifdef EXEC_TEMPFILE_ONLY
       if(ascii_strncasecmp(tok,"use-temp-files",14)==0 ||
              ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
-       log_info(_("WARNING: keyserver option `%s' is not used"
+       log_info(_("WARNING: keyserver option '%s' is not used"
                   " on this platform\n"),tok);
 #else
       if(ascii_strncasecmp(tok,"use-temp-files",14)==0)
@@ -505,9 +508,11 @@ print_keyrec(int number,struct keyrec *keyrec)
 
   if(keyrec->type)
     {
-      const char *str = gcry_pk_algo_name (keyrec->type);
+      const char *str;
+
+      str = openpgp_pk_algo_name (keyrec->type);
 
-      if(str)
+      if (str && strcmp (str, "?"))
        es_printf ("%s ",str);
       else
        es_printf ("unknown ");
@@ -526,21 +531,27 @@ print_keyrec(int number,struct keyrec *keyrec)
       break;
 
       /* However, if it gave us a long keyid, we can honor
-        --keyid-format */
+        --keyid-format via keystr(). */
     case KEYDB_SEARCH_MODE_LONG_KID:
       es_printf ("key %s",keystr(keyrec->desc.u.kid));
       break;
 
+      /* If it gave us a PGP 2.x fingerprint, not much we can do
+        beyond displaying it. */
     case KEYDB_SEARCH_MODE_FPR16:
       es_printf ("key ");
       for(i=0;i<16;i++)
        es_printf ("%02X",keyrec->desc.u.fpr[i]);
       break;
 
+      /* If we get a modern fingerprint, we have the most
+        flexibility. */
     case KEYDB_SEARCH_MODE_FPR20:
-      es_printf ("key ");
-      for(i=0;i<20;i++)
-       es_printf ("%02X", keyrec->desc.u.fpr[i]);
+      {
+       u32 kid[2];
+       keyid_from_fingerprint(keyrec->desc.u.fpr,20,kid);
+       es_printf("key %s",keystr(kid));
+      }
       break;
 
     default:
@@ -810,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);
       }
     }
@@ -822,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");
@@ -842,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
@@ -1018,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)
 {
@@ -1050,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)
@@ -1076,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
@@ -1091,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 */
@@ -1133,7 +1254,7 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
        }
     }
 
-  while (!(rc = keydb_search (kdbhd, desc, ndesc)))
+  while (!(rc = keydb_search (kdbhd, desc, ndesc, NULL)))
     {
       if (!users)
        desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
@@ -1236,8 +1357,8 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
        }
     }
 
-  if(rc==-1)
-    rc=0;
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+    rc = 0;
 
  leave:
   if(rc)
@@ -1295,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]),
@@ -1325,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);
@@ -1335,7 +1456,7 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
   /* If the original options didn't have fast import, and the trustdb
      is dirty, rebuild. */
   if(!(opt.keyserver_options.import_options&IMPORT_FAST))
-    trustdb_check_or_update();
+    check_or_update_trustdb ();
 
   return rc;
 }
@@ -1416,13 +1537,13 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
   /* switch(ret) */
   /*   { */
   /*   case KEYSERVER_SCHEME_NOT_FOUND: */
-  /*     log_error(_("no handler for keyserver scheme `%s'\n"), */
+  /*     log_error(_("no handler for keyserver scheme '%s'\n"), */
   /*               opt.keyserver->scheme); */
   /*     break; */
 
   /*   case KEYSERVER_NOT_SUPPORTED: */
-  /*     log_error(_("action `%s' not supported with keyserver " */
-  /*                 "scheme `%s'\n"), "search", opt.keyserver->scheme); */
+  /*     log_error(_("action '%s' not supported with keyserver " */
+  /*                 "scheme '%s'\n"), "search", opt.keyserver->scheme); */
   /*     break; */
 
   /*   case KEYSERVER_TIMEOUT: */
@@ -1448,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.  */
@@ -1514,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
@@ -1552,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();
 
@@ -1569,16 +1691,24 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
          harmless to ignore them, but ignoring them does make gpg
          complain about "no valid OpenPGP data found".  One way to do
          this could be to continue parsing this line-by-line and make
-         a temp iobuf for each key. */
-
-      import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
-                             opt.keyserver_options.import_options);
+         a temp iobuf for each key.  Note that we don't allow the
+         import of secret keys from a keyserver.  Keyservers should
+         never accept or send them but we better protect against rogue
+         keyservers. */
+
+      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),
+                             keyserver_retrieval_screener, &screenerarg);
 
       import_print_stats (stats_handle);
       import_release_stats_handle (stats_handle);
     }
   es_fclose (datastream);
-
+  xfree (source);
 
   return err;
 }
@@ -1638,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.
@@ -1654,7 +1787,7 @@ keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
   for (sl=urilist; sl; sl=sl->next)
     {
       if (!opt.quiet)
-        log_info (_("requesting key from `%s'\n"), sl->d);
+        log_info (_("requesting key from '%s'\n"), sl->d);
 
       err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream);
       if (!err)
@@ -1663,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);
@@ -1674,12 +1808,12 @@ 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. */
   if (!(opt.keyserver_options.import_options&IMPORT_FAST))
-    trustdb_check_or_update ();
+    check_or_update_trustdb ();
 
   return 0;
 }
@@ -1690,9 +1824,10 @@ int
 keyserver_import_cert (ctrl_t ctrl,
                        const char *name,unsigned char **fpr,size_t *fpr_len)
 {
+  gpg_error_t err;
   char *domain,*look,*url;
-  IOBUF key;
-  int type,rc=G10ERR_GENERAL;
+  estream_t key;
+
 
   look=xstrdup(name);
 
@@ -1700,30 +1835,27 @@ keyserver_import_cert (ctrl_t ctrl,
   if(domain)
     *domain='.';
 
-  type=get_dns_cert(look,max_cert_size,&key,fpr,fpr_len,&url);
-  if (!type || type == -1)
-    {
-      /* There might be an error in res_query which leads to an error
-         return (-1) in the case that nothing was found.  Thus we take
-         all errors as key not found.  */
-      rc = G10ERR_NO_PUBKEY;
-    }
-  else if (type==1)
+  err = get_dns_cert (look, &key, fpr, fpr_len, &url);
+  if (err)
+    ;
+  else if (key)
     {
       int armor_status=opt.no_armor;
 
       /* CERTs are always in binary format */
       opt.no_armor=1;
 
-      /* FIXME: Pass CTRL.  */
-      rc = import_keys_stream (NULL, key, NULL, fpr, fpr_len,
-                               opt.keyserver_options.import_options);
+      err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len,
+                                   (opt.keyserver_options.import_options
+                                    | IMPORT_NO_SECKEY),
+                                   NULL, NULL);
 
       opt.no_armor=armor_status;
 
-      iobuf_close(key);
+      es_fclose (key);
+      key = NULL;
     }
-  else if(type==2 && *fpr)
+  else if (*fpr)
     {
       /* We only consider the IPGP type if a fingerprint was provided.
         This lets us select the right key regardless of what a URL
@@ -1735,7 +1867,7 @@ keyserver_import_cert (ctrl_t ctrl,
          spec=parse_keyserver_uri(url,1,NULL,0);
          if(spec)
            {
-             rc = keyserver_import_fprint (ctrl, *fpr,*fpr_len,spec);
+             err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,spec);
              free_keyserver_spec(spec);
            }
        }
@@ -1744,7 +1876,7 @@ keyserver_import_cert (ctrl_t ctrl,
          /* If only a fingerprint is provided, try and fetch it from
             our --keyserver */
 
-         rc = keyserver_import_fprint (ctrl, *fpr,*fpr_len,opt.keyserver);
+         err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,opt.keyserver);
        }
       else
        log_info(_("no keyserver known (use option --keyserver)\n"));
@@ -1753,12 +1885,12 @@ keyserver_import_cert (ctrl_t ctrl,
         found, but no keyserver" " known (use option
         --keyserver)\n" ? */
 
-      xfree(url);
     }
 
+  xfree(url);
   xfree(look);
 
-  return rc;
+  return err;
 }
 
 /* Import key pointed to by a PKA record. Return the requested
@@ -1796,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;
@@ -1891,4 +2010,5 @@ keyserver_import_ldap (ctrl_t ctrl,
   free_keyserver_spec(keyserver);
 
   return rc;
+#endif
 }