g10: Fix segfault on unsupported curve.
[gnupg.git] / g10 / keyserver.c
index 102f65d..e9ccb58 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.
  *
 #include "trustdb.h"
 #include "keyserver-internal.h"
 #include "util.h"
-#include "dns-cert.h"
-#include "pka.h"
-#ifdef USE_DNS_SRV
-#include "srv.h"
-#endif
 #include "membuf.h"
 #include "call-dirmngr.h"
 
@@ -90,13 +86,14 @@ static struct parse_options keyserver_opts[]=
   {
     /* some of these options are not real - just for the help
        message */
-    {"max-cert-size",0,NULL,NULL},
+    {"max-cert-size",0,NULL,NULL},  /* MUST be the first in this array! */
+    {"http-proxy", KEYSERVER_HTTP_PROXY, NULL, /* MUST be the second!  */
+     N_("override proxy options set for dirmngr")},
+
     {"include-revoked",0,NULL,N_("include revoked keys in search results")},
     {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")},
-    {"use-temp-files",0,NULL,
-     N_("use temporary files to pass data to keyserver helpers")},
-    {"keep-temp-files",KEYSERVER_KEEP_TEMP_FILES,NULL,
-     N_("do not delete temporary files after using them")},
+    {"timeout", KEYSERVER_TIMEOUT, NULL,
+     N_("override timeout options set for dirmngr")},
     {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL,
      NULL},
     {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL,
@@ -110,9 +107,9 @@ 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);
-static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
-                                  struct keyserver_spec *keyserver);
+                                  struct keyserver_spec *override_keyserver,
+                                  unsigned char **r_fpr, size_t *r_fprlen);
+static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs);
 
 
 /* Reasonable guess.  The commonly used test key simon.josefsson.org
@@ -121,27 +118,28 @@ static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
 
 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE;
 
+
 static void
-add_canonical_option(char *option,strlist_t *list)
+warn_kshelper_option(char *option, int noisy)
 {
-  char *arg=argsplit(option);
-
-  if(arg)
-    {
-      char *joined;
-
-      joined=xmalloc(strlen(option)+1+strlen(arg)+1);
-      /* Make a canonical name=value form with no spaces */
-      strcpy(joined,option);
-      strcat(joined,"=");
-      strcat(joined,arg);
-      append_to_strlist(list,joined);
-      xfree(joined);
-    }
-  else
-    append_to_strlist(list,option);
+  char *p;
+
+  if ((p=strchr (option, '=')))
+    *p = 0;
+
+  if (!strcmp (option, "ca-cert-file"))
+    log_info ("keyserver option '%s' is obsolete; please use "
+              "'%s' in dirmngr.conf\n",
+              "ca-cert-file", "hkp-cacert");
+  else if (!strcmp (option, "check-cert")
+           || !strcmp (option, "broken-http-proxy"))
+    log_info ("keyserver option '%s' is obsolete\n", option);
+  else if (noisy || opt.verbose)
+    log_info ("keyserver option '%s' is unknown\n", option);
 }
 
+
+/* Called from main to parse the args for --keyserver-options.  */
 int
 parse_keyserver_options(char *options)
 {
@@ -150,47 +148,24 @@ parse_keyserver_options(char *options)
   char *max_cert=NULL;
 
   keyserver_opts[0].value=&max_cert;
+  keyserver_opts[1].value=&opt.keyserver_options.http_proxy;
 
   while((tok=optsep(&options)))
     {
       if(tok[0]=='\0')
        continue;
 
-      /* For backwards compatibility.  1.2.x used honor-http-proxy and
-        there are a good number of documents published that recommend
-        it. */
-      if(ascii_strcasecmp(tok,"honor-http-proxy")==0)
-       tok="http-proxy";
-      else if(ascii_strcasecmp(tok,"no-honor-http-proxy")==0)
-       tok="no-http-proxy";
-
       /* We accept quite a few possible options here - some options to
         handle specially, the keyserver_options list, and import and
-        export options that pertain to keyserver operations.  Note
-        that you must use strncasecmp here as there might be an
-        =argument attached which will foil the use of strcasecmp. */
-
-#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"
-                  " on this platform\n"),tok);
-#else
-      if(ascii_strncasecmp(tok,"use-temp-files",14)==0)
-       opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
-      else if(ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
-       opt.keyserver_options.options&=~KEYSERVER_USE_TEMP_FILES;
-#endif
-      else if(!parse_options(tok,&opt.keyserver_options.options,
-                            keyserver_opts,0)
-        && !parse_import_options(tok,
-                                 &opt.keyserver_options.import_options,0)
-        && !parse_export_options(tok,
-                                 &opt.keyserver_options.export_options,0))
+        export options that pertain to keyserver operations.  */
+
+      if (!parse_options (tok,&opt.keyserver_options.options, keyserver_opts,0)
+          && !parse_import_options(tok,&opt.keyserver_options.import_options,0)
+          && !parse_export_options(tok,&opt.keyserver_options.export_options,0))
        {
-         /* All of the standard options have failed, so the option is
-            destined for a keyserver plugin. */
-         add_canonical_option(tok,&opt.keyserver_options.other);
+         /* All of the standard options have failed, so the option was
+            destined for a keyserver plugin as used by GnuPG < 2.1 */
+         warn_kshelper_option (tok, 1);
        }
     }
 
@@ -205,6 +180,7 @@ parse_keyserver_options(char *options)
   return ret;
 }
 
+
 void
 free_keyserver_spec(struct keyserver_spec *keyserver)
 {
@@ -259,8 +235,7 @@ keyserver_match(struct keyserver_spec *spec)
    keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
 
 keyserver_spec_t
-parse_keyserver_uri (const char *string,int require_scheme,
-                    const char *configname,unsigned int configlineno)
+parse_keyserver_uri (const char *string,int require_scheme)
 {
   int assume_hkp=0;
   struct keyserver_spec *keyserver;
@@ -283,7 +258,7 @@ parse_keyserver_uri (const char *string,int require_scheme,
       options++;
 
       while((tok=optsep(&options)))
-       add_canonical_option(tok,&keyserver->options);
+       warn_kshelper_option (tok, 0);
     }
 
   /* Get the scheme */
@@ -342,11 +317,8 @@ parse_keyserver_uri (const char *string,int require_scheme,
 
   if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0)
     {
-      deprecated_warning(configname,configlineno,"x-broken-hkp",
-                        "--keyserver-options ","broken-http-proxy");
-      xfree(keyserver->scheme);
-      keyserver->scheme=xstrdup("hkp");
-      append_to_strlist(&opt.keyserver_options.other,"broken-http-proxy");
+      log_info ("keyserver option '%s' is obsolete\n",
+                "x-broken-hkp");
     }
   else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0)
     {
@@ -485,7 +457,7 @@ parse_preferred_keyserver(PKT_signature *sig)
 
       memcpy(dupe,p,plen);
       dupe[plen]='\0';
-      spec=parse_keyserver_uri(dupe,1,NULL,0);
+      spec = parse_keyserver_uri (dupe, 1);
       xfree(dupe);
     }
 
@@ -506,9 +478,11 @@ print_keyrec(int number,struct keyrec *keyrec)
 
   if(keyrec->type)
     {
-      const char *str = gcry_pk_algo_name (keyrec->type);
+      const char *str;
 
-      if(str)
+      str = openpgp_pk_algo_name (keyrec->type);
+
+      if (str && strcmp (str, "?"))
        es_printf ("%s ",str);
       else
        es_printf ("unknown ");
@@ -527,21 +501,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:
@@ -811,7 +791,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);
       }
     }
@@ -823,17 +803,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");
@@ -843,7 +835,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
@@ -1012,13 +1004,93 @@ keyserver_export (ctrl_t ctrl, strlist_t users)
 
   if(sl)
     {
-      rc = keyserver_put (ctrl, sl, opt.keyserver);
+      rc = keyserver_put (ctrl, sl);
       free_strlist(sl);
     }
 
   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)
 {
@@ -1051,13 +1123,39 @@ 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;
 }
 
+
+/* Return true if any keyserver has been configured. */
+int
+keyserver_any_configured (ctrl_t ctrl)
+{
+  return !gpg_dirmngr_ks_list (ctrl, NULL);
+}
+
+
+/* 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)
@@ -1077,7 +1175,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
@@ -1092,24 +1190,33 @@ 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 */
 static int
 keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
 {
-  int rc=0,ndesc,num=100;
-  KBNODE keyblock=NULL,node;
+  int rc = 0;
+  int num = 100;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
   KEYDB_HANDLE kdbhd;
-  KEYDB_SEARCH_DESC *desc;
+  int ndesc;
+  KEYDB_SEARCH_DESC *desc = NULL;
   strlist_t sl;
 
   *count=0;
 
   *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
 
-  kdbhd=keydb_new ();
+  kdbhd = keydb_new ();
+  if (!kdbhd)
+    {
+      rc = gpg_error_from_syserror ();
+      goto leave;
+    }
+  keydb_disable_caching (kdbhd);  /* We are looping the search.  */
 
   if(!users)
     {
@@ -1134,8 +1241,12 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
        }
     }
 
-  while (!(rc = keydb_search (kdbhd, desc, ndesc)))
+  for (;;)
     {
+      rc = keydb_search (kdbhd, desc, ndesc, NULL);
+      if (rc)
+        break;  /* ready.  */
+
       if (!users)
        desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
 
@@ -1143,7 +1254,7 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
       rc = keydb_get_keyblock (kdbhd, &keyblock );
       if( rc )
        {
-         log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+          log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
          goto leave;
        }
 
@@ -1242,7 +1353,10 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
 
  leave:
   if(rc)
-    xfree(*klist);
+    {
+      xfree(*klist);
+      *klist = NULL;
+    }
   xfree(desc);
   keydb_release(kdbhd);
   release_kbnode(keyblock);
@@ -1253,10 +1367,12 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
 /* Note this is different than the original HKP refresh.  It allows
    usernames to refresh only part of the keyring. */
 
-int
+gpg_error_t
 keyserver_refresh (ctrl_t ctrl, strlist_t users)
 {
-  int rc,count,numdesc,fakev3=0;
+  gpg_error_t err;
+  int count, numdesc;
+  int fakev3 = 0;
   KEYDB_SEARCH_DESC *desc;
   unsigned int options=opt.keyserver_options.import_options;
 
@@ -1271,15 +1387,20 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
   opt.keyserver_options.import_options|=IMPORT_FAST;
 
   /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO
-     scheme, then enable fake v3 keyid generation. */
+     scheme, then enable fake v3 keyid generation.  Note that this
+     works only with a keyserver configured. gpg.conf
+     (i.e. opt.keyserver); however that method of configuring a
+     keyserver is deprecated and in any case it is questionable
+     whether we should keep on supporting these ancient and broken
+     keyservers.  */
   if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver
      && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 ||
         ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0))
     fakev3=1;
 
-  rc=keyidlist(users,&desc,&numdesc,fakev3);
-  if(rc)
-    return rc;
+  err = keyidlist (users, &desc, &numdesc, fakev3);
+  if (err)
+    return err;
 
   count=numdesc;
   if(count>0)
@@ -1293,14 +1414,17 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
            {
              struct keyserver_spec *keyserver=desc[i].skipfncvalue;
 
+              if (!opt.quiet)
+                log_info (_("refreshing %d key from %s\n"), 1, keyserver->uri);
+
              /* 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);
-             if(rc)
+             err = keyserver_get (ctrl, &desc[i], 1, keyserver, NULL, NULL);
+             if (err)
                log_info(_("WARNING: unable to refresh key %s"
                           " via %s: %s\n"),keystr_from_desc(&desc[i]),
-                        keyserver->uri,g10_errstr(rc));
+                        keyserver->uri,gpg_strerror (err));
              else
                {
                  /* We got it, so mark it as NONE so we don't try and
@@ -1317,16 +1441,21 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
 
   if(count>0)
     {
-      if(opt.keyserver)
-       {
-         if(count==1)
-           log_info(_("refreshing 1 key from %s\n"),opt.keyserver->uri);
-         else
-           log_info(_("refreshing %d keys from %s\n"),
-                    count,opt.keyserver->uri);
-       }
+      char *tmpuri;
+
+      err = gpg_dirmngr_ks_list (ctrl, &tmpuri);
+      if (!err)
+        {
+          if (!opt.quiet)
+            {
+              log_info (ngettext("refreshing %d key from %s\n",
+                                 "refreshing %d keys from %s\n",
+                                 count), count, tmpuri);
+            }
+          xfree (tmpuri);
 
-      rc=keyserver_get (ctrl, desc, numdesc, NULL);
+          err = keyserver_get (ctrl, desc, numdesc, NULL, NULL, NULL);
+        }
     }
 
   xfree(desc);
@@ -1336,9 +1465,9 @@ 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;
+  return err;
 }
 
 
@@ -1356,21 +1485,15 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
   if (!tokens)
     return 0;  /* Return success if no patterns are given.  */
 
-  if (!opt.keyserver)
-    {
-      log_error (_("no keyserver known (use option --keyserver)\n"));
-      return gpg_error (GPG_ERR_NO_KEYSERVER);
-    }
-
   /* Write global options */
 
   /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */
-  /*   fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
+  /*   es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
 
   /* Write per-keyserver options */
 
   /* for(temp=keyserver->options;temp;temp=temp->next) */
-  /*   fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
+  /*   es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
 
   {
     membuf_t mb;
@@ -1417,13 +1540,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: */
@@ -1447,35 +1570,43 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
   return err;
 }
 
-
-
-/* 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);
-
- */
-
+/* Helper for keyserver_get.  Here we only receive a chunk of the
+   description to be processed in one batch.  This is required due to
+   the limited number of patterns the dirmngr interface (KS_GET) can
+   grok and to limit the amount of temporary required memory.  */
 static gpg_error_t
-keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
-               struct keyserver_spec *keyserver)
+keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
+                     int *r_ndesc_used,
+                     import_stats_t stats_handle,
+                     struct keyserver_spec *override_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;
+  size_t linelen;  /* Estimated linelen for KS_GET.  */
+  size_t n;
+
+#define MAX_KS_GET_LINELEN 950  /* Somewhat lower than the real limit.  */
+
+  *r_ndesc_used = 0;
 
   /* Create an array filled with a search pattern for each key.  The
      array is delimited by a NULL entry.  */
   pattern = xtrycalloc (ndesc+1, sizeof *pattern);
   if (!pattern)
     return gpg_error_from_syserror ();
+
+  /* Note that we break the loop as soon as our estimation of the to
+     be used line length reaches the limit.  But we do this only if we
+     have processed at leas one search requests so that an overlong
+     single request will be rejected only later by gpg_dirmngr_ks_get
+     but we are sure that R_NDESC_USED has been updated.  This avoids
+     a possible indefinite loop.  */
+  linelen = 9; /* "KS_GET --" */
   for (npat=idx=0; idx < ndesc; idx++)
     {
       int quiet = 0;
@@ -1483,7 +1614,12 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
       if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20
           || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16)
         {
-          pattern[npat] = xtrymalloc (2+2*20+1);
+          n = 1+2+2*20;
+          if (idx && linelen + n > MAX_KS_GET_LINELEN)
+            break; /* Declare end of this chunk.  */
+          linelen += n;
+
+          pattern[npat] = xtrymalloc (n);
           if (!pattern[npat])
             err = gpg_error_from_syserror ();
           else
@@ -1497,6 +1633,11 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
         }
       else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID)
         {
+          n = 1+2+16;
+          if (idx && linelen + n > MAX_KS_GET_LINELEN)
+            break; /* Declare end of this chunk.  */
+          linelen += n;
+
           pattern[npat] = xtryasprintf ("0x%08lX%08lX",
                                         (ulong)desc[idx].u.kid[0],
                                         (ulong)desc[idx].u.kid[1]);
@@ -1507,6 +1648,11 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
         }
       else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID)
         {
+          n = 1+2+8;
+          if (idx && linelen + n > MAX_KS_GET_LINELEN)
+            break; /* Declare end of this chunk.  */
+          linelen += n;
+
           pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]);
           if (!pattern[npat])
             err = gpg_error_from_syserror ();
@@ -1515,10 +1661,18 @@ 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 also uses 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.)  */
+
+          n = 1+1+strlen (desc[idx].u.name);
+          if (idx && linelen + n > MAX_KS_GET_LINELEN)
+            break; /* Declare end of this chunk.  */
+          linelen += n;
+
+          pattern[npat] = strconcat ("=", desc[idx].u.name, NULL);
           if (!pattern[npat])
             err = gpg_error_from_syserror ();
           else
@@ -1540,28 +1694,33 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
           return err;
         }
 
-      if (!quiet && keyserver)
+      if (!quiet && override_keyserver)
         {
-          if (keyserver->host)
+          if (override_keyserver->host)
             log_info (_("requesting key %s from %s server %s\n"),
                       keystr_from_desc (&desc[idx]),
-                      keyserver->scheme, keyserver->host);
+                      override_keyserver->scheme, override_keyserver->host);
           else
             log_info (_("requesting key %s from %s\n"),
-                      keystr_from_desc (&desc[idx]), keyserver->uri);
+                      keystr_from_desc (&desc[idx]), override_keyserver->uri);
         }
     }
 
+  /* Remember now many of search items were considered.  Note that
+     this is different from NPAT.  */
+  *r_ndesc_used = idx;
 
-  err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream);
+  err = gpg_dirmngr_ks_get (ctrl, pattern, override_keyserver,
+                            &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;
-
-      stats_handle = import_new_stats_handle();
+      struct ks_retrieval_screener_arg_s screenerarg;
 
       /* FIXME: Check whether this comment should be moved to dirmngr.
 
@@ -1570,36 +1729,79 @@ 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. */
+         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 = *r_ndesc_used;
+      import_keys_es_stream (ctrl, datastream, stats_handle,
+                             r_fpr, r_fprlen,
+                             (opt.keyserver_options.import_options
+                              | IMPORT_NO_SECKEY),
+                             keyserver_retrieval_screener, &screenerarg);
+    }
+  es_fclose (datastream);
+  xfree (source);
+
+  return err;
+}
 
-      import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
-                             opt.keyserver_options.import_options);
 
-      import_print_stats (stats_handle);
-      import_release_stats_handle (stats_handle);
+/* Retrieve a key from a keyserver.  The search pattern are in
+   (DESC,NDESC).  Allowed search modes are keyid, fingerprint, and
+   exact searches.  OVERRIDE_KEYSERVER gives an optional override
+   keyserver. If (R_FPR,R_FPRLEN) are not NULL, they may return the
+   fingerprint of a single imported key.  */
+static gpg_error_t
+keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
+               struct keyserver_spec *override_keyserver,
+               unsigned char **r_fpr, size_t *r_fprlen)
+{
+  gpg_error_t err;
+  import_stats_t stats_handle;
+  int ndesc_used;
+  int any_good = 0;
+
+  stats_handle = import_new_stats_handle();
+
+  for (;;)
+    {
+      err = keyserver_get_chunk (ctrl, desc, ndesc, &ndesc_used, stats_handle,
+                                 override_keyserver, r_fpr, r_fprlen);
+      if (!err)
+        any_good = 1;
+      if (err || ndesc_used >= ndesc)
+        break; /* Error or all processed.  */
+      /* Prepare for the next chunk.  */
+      desc += ndesc_used;
+      ndesc -= ndesc_used;
     }
-  es_fclose (datastream);
 
+  if (any_good)
+    import_print_stats (stats_handle);
 
+  import_release_stats_handle (stats_handle);
   return err;
 }
 
 
-/* Send all keys specified by KEYSPECS to the KEYSERVERS.  */
+/* Send all keys specified by KEYSPECS to the configured keyserver.  */
 static gpg_error_t
-keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
-               struct keyserver_spec *keyserver)
+keyserver_put (ctrl_t ctrl, strlist_t keyspecs)
 
 {
   gpg_error_t err;
   strlist_t kspec;
+  char *ksurl;
 
   if (!keyspecs)
     return 0;  /* Return success if the list is empty.  */
 
-  if (!opt.keyserver)
+  if (gpg_dirmngr_ks_list (ctrl, &ksurl))
     {
-      log_error (_("no keyserver known (use option --keyserver)\n"));
+      log_error (_("no keyserver known\n"));
       return gpg_error (GPG_ERR_NO_KEYSERVER);
     }
 
@@ -1611,41 +1813,44 @@ keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
 
       err = export_pubkey_buffer (ctrl, kspec->d,
                                   opt.keyserver_options.export_options,
+                                  NULL,
                                   &keyblock, &data, &datalen);
       if (err)
         log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err));
       else
         {
-          if (keyserver->host)
-            log_info (_("sending key %s to %s server %s\n"),
-                      keystr (keyblock->pkt->pkt.public_key->keyid),
-                      keyserver->scheme, keyserver->host);
-          else
-            log_info (_("sending key %s to %s\n"),
-                      keystr (keyblock->pkt->pkt.public_key->keyid),
-                      keyserver->uri);
+          log_info (_("sending key %s to %s\n"),
+                    keystr (keyblock->pkt->pkt.public_key->keyid),
+                    ksurl?ksurl:"[?]");
 
           err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock);
           release_kbnode (keyblock);
           xfree (data);
           if (err)
-            log_error (_("keyserver send failed: %s\n"), gpg_strerror (err));
+            {
+              write_status_error ("keyserver_send", err);
+              log_error (_("keyserver send failed: %s\n"), gpg_strerror (err));
+            }
         }
     }
 
+  xfree (ksurl);
 
   return err;
 
 }
 
 
+/* 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.
@@ -1655,16 +1860,17 @@ 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)
         {
-          void *stats_handle;
+          import_stats_t stats_handle;
 
           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);
@@ -1675,45 +1881,51 @@ 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;
 }
 
 
-/* Import key in a CERT or pointed to by a CERT */
+/* Import key in a CERT or pointed to by a CERT.  In DANE_MODE fetch
+   the certificate using the DANE method.  */
 int
-keyserver_import_cert (ctrl_t ctrl,
-                       const char *name,unsigned char **fpr,size_t *fpr_len)
+keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode,
+                       unsigned char **fpr,size_t *fpr_len)
 {
   gpg_error_t err;
-  char *domain,*look,*url;
+  char *look,*url;
   estream_t key;
 
+  look = xstrdup(name);
 
-  look=xstrdup(name);
-
-  domain=strrchr(look,'@');
-  if(domain)
-    *domain='.';
+  if (!dane_mode)
+    {
+      char *domain = strrchr (look,'@');
+      if (domain)
+        *domain='.';
+    }
 
-  err = get_dns_cert (look, &key, fpr, fpr_len, &url);
+  err = gpg_dirmngr_dns_cert (ctrl, look, dane_mode? NULL : "*",
+                              &key, fpr, fpr_len, &url);
   if (err)
     ;
   else if (key)
     {
       int armor_status=opt.no_armor;
 
-      /* CERTs are always in binary format */
+      /* CERTs and DANE records are always in binary format */
       opt.no_armor=1;
 
       err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len,
-                                  opt.keyserver_options.import_options);
+                                   (opt.keyserver_options.import_options
+                                    | IMPORT_NO_SECKEY),
+                                   NULL, NULL);
 
       opt.no_armor=armor_status;
 
@@ -1729,22 +1941,22 @@ keyserver_import_cert (ctrl_t ctrl,
        {
          struct keyserver_spec *spec;
 
-         spec=parse_keyserver_uri(url,1,NULL,0);
+         spec = parse_keyserver_uri (url, 1);
          if(spec)
            {
              err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,spec);
              free_keyserver_spec(spec);
            }
        }
-      else if(opt.keyserver)
+      else if (keyserver_any_configured (ctrl))
        {
          /* If only a fingerprint is provided, try and fetch it from
-            our --keyserver */
+            the configured keyserver. */
 
          err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,opt.keyserver);
        }
       else
-       log_info(_("no keyserver known (use option --keyserver)\n"));
+       log_info(_("no keyserver known\n"));
 
       /* Give a better string here? "CERT fingerprint for \"%s\"
         found, but no keyserver" " known (use option
@@ -1760,64 +1972,49 @@ keyserver_import_cert (ctrl_t ctrl,
 
 /* Import key pointed to by a PKA record. Return the requested
    fingerprint in fpr. */
-int
-keyserver_import_pka (ctrl_t ctrl,
-                      const char *name,unsigned char **fpr,size_t *fpr_len)
+gpg_error_t
+keyserver_import_pka (ctrl_t ctrl, const char *name,
+                      unsigned char **fpr, size_t *fpr_len)
 {
-  char *uri;
-  int rc = G10ERR_NO_PUBKEY;
-
-  *fpr = xmalloc (20);
-  *fpr_len = 20;
+  gpg_error_t err;
+  char *url;
 
-  uri = get_pka_info (name, *fpr);
-  if (uri && *uri)
+  err = gpg_dirmngr_get_pka (ctrl, name, fpr, fpr_len, &url);
+  if (url && *url && fpr && fpr_len)
     {
-      /* An URI is available.  Lookup the key. */
+      /* An URL is available.  Lookup the key. */
       struct keyserver_spec *spec;
-      spec = parse_keyserver_uri (uri, 1, NULL, 0);
+      spec = parse_keyserver_uri (url, 1);
       if (spec)
        {
-         rc = keyserver_import_fprint (ctrl, *fpr, 20, spec);
+         err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec);
          free_keyserver_spec (spec);
        }
-      xfree (uri);
     }
+  xfree (url);
 
-  if (rc)
+  if (err)
     {
       xfree(*fpr);
       *fpr = NULL;
+      *fpr_len = 0;
     }
 
-  return rc;
+  return err;
 }
 
-/* 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;
@@ -1831,7 +2028,7 @@ keyserver_import_ldap (ctrl_t ctrl,
   /* Parse out the domain */
   domain=strrchr(name,'@');
   if(!domain)
-    return G10ERR_GENERAL;
+    return GPG_ERR_GENERAL;
 
   domain++;
 
@@ -1843,6 +2040,7 @@ keyserver_import_ldap (ctrl_t ctrl,
 #ifdef USE_DNS_SRV
   snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain);
 
+  FIXME("network related - move to dirmngr or drop the code");
   srvcount=getsrv(srvname,&srvlist);
 
   for(i=0;i<srvcount;i++)
@@ -1888,4 +2086,5 @@ keyserver_import_ldap (ctrl_t ctrl,
   free_keyserver_spec(keyserver);
 
   return rc;
+#endif
 }