build: Update config.{guess,sub} to {2016-05-15,2016-06-20}.
[gnupg.git] / g10 / call-dirmngr.c
index 10dcb20..75a7f46 100644 (file)
@@ -25,7 +25,6 @@
 #include <errno.h>
 #include <unistd.h>
 #include <time.h>
-#include <assert.h>
 #ifdef HAVE_LOCALE_H
 # include <locale.h>
 #endif
@@ -38,6 +37,7 @@
 #include "i18n.h"
 #include "asshelp.h"
 #include "keyserver.h"
+#include "status.h"
 #include "call-dirmngr.h"
 
 
@@ -132,6 +132,40 @@ gpg_dirmngr_deinit_session_data (ctrl_t ctrl)
 }
 
 
+/* Print a warning if the server's version number is less than our
+   version number.  Returns an error code on a connection problem.  */
+static gpg_error_t
+warn_version_mismatch (assuan_context_t ctx, const char *servername)
+{
+  gpg_error_t err;
+  char *serverversion;
+  const char *myversion = strusage (13);
+
+  err = get_assuan_server_version (ctx, 0, &serverversion);
+  if (err)
+    log_error (_("error getting version from '%s': %s\n"),
+               servername, gpg_strerror (err));
+  else if (!compare_version_strings (serverversion, myversion))
+    {
+      char *warn;
+
+      warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"),
+                           servername, serverversion, myversion);
+      if (!warn)
+        err = gpg_error_from_syserror ();
+      else
+        {
+          log_info (_("WARNING: %s\n"), warn);
+          write_status_strings (STATUS_WARNING, "server_version_mismatch 0",
+                                " ", warn, NULL);
+          xfree (warn);
+        }
+    }
+  xfree (serverversion);
+  return err;
+}
+
+
 /* Try to connect to the Dirmngr via a socket or spawn it if possible.
    Handle the server's initial greeting and set global options.  */
 static gpg_error_t
@@ -143,7 +177,6 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
   *r_ctx = NULL;
   err = start_new_dirmngr (&ctx,
                            GPG_ERR_SOURCE_DEFAULT,
-                           opt.homedir,
                            opt.dirmngr_program,
                            opt.autostart, opt.verbose, DBG_IPC,
                            NULL /*gpg_status2*/, ctrl);
@@ -157,7 +190,7 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
           log_info (_("no dirmngr running in this session\n"));
         }
     }
-  else if (!err)
+  else if (!err && !(err = warn_version_mismatch (ctx, DIRMNGR_NAME)))
     {
       char *line;
 
@@ -183,13 +216,13 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
       else if ((opt.keyserver_options.options & KEYSERVER_HONOR_KEYSERVER_URL))
         {
           /* Tell the dirmngr that this possibly privacy invading
-             option is in use.  If Dirmngr is running in TOR mode, it
+             option is in use.  If Dirmngr is running in Tor mode, it
              will return an error.  */
           err = assuan_transact (ctx, "OPTION honor-keyserver-url-used",
                                  NULL, NULL, NULL, NULL, NULL, NULL);
           if (gpg_err_code (err) == GPG_ERR_FORBIDDEN)
             log_error (_("keyserver option \"honor-keyserver-url\""
-                         " may not be used in TOR mode\n"));
+                         " may not be used in Tor mode\n"));
           else if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
             err = 0; /* Old dirmngr versions do not support this option.  */
         }
@@ -226,7 +259,7 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
       if (dml)
         {
           /* Found an inactive local session - return that.  */
-          assert (!dml->is_active);
+          log_assert (!dml->is_active);
 
           /* But first do the per session init if not yet done.  */
           if (!dml->set_keyservers_done)
@@ -369,7 +402,8 @@ gpg_dirmngr_ks_list (ctrl_t ctrl, char **r_keyserver)
 
   memset (&stparm, 0, sizeof stparm);
   stparm.keyword = "KEYSERVER";
-  *r_keyserver = NULL;
+  if (r_keyserver)
+    *r_keyserver = NULL;
 
   err = open_context (ctrl, &ctx);
   if (err)
@@ -385,7 +419,10 @@ gpg_dirmngr_ks_list (ctrl_t ctrl, char **r_keyserver)
       goto leave;
     }
 
-  *r_keyserver = stparm.source;
+  if (r_keyserver)
+    *r_keyserver = stparm.source;
+  else
+    xfree (stparm.source);
   stparm.source = NULL;
 
  leave:
@@ -765,7 +802,7 @@ record_output (estream_t output,
       type_str = "sig";
       break;
     default:
-      assert (! "Unhandled type.");
+      log_assert (! "Unhandled type.");
     }
 
   if (pub_key_length > 0)
@@ -1025,7 +1062,7 @@ gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock)
 
 
 \f
-/* Data callback for the DNS_CERT command. */
+/* Data callback for the DNS_CERT and WKD_GET commands. */
 static gpg_error_t
 dns_cert_data_cb (void *opaque, const void *data, size_t datalen)
 {
@@ -1080,10 +1117,8 @@ dns_cert_status_cb (void *opaque, const char *line)
     {
       if (parm->url)
         err = gpg_error (GPG_ERR_DUP_KEY);
-      else if (!(parm->fpr = xtrymalloc (nbytes)))
+      else if (!(parm->url = xtrystrdup (s)))
         err = gpg_error_from_syserror ();
-      else
-        memcpy (parm->fpr, line, (parm->fprlen = nbytes));
     }
 
   return err;
@@ -1250,3 +1285,62 @@ gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid,
   close_context (ctrl, ctx);
   return err;
 }
+
+
+\f
+/* Ask the dirmngr to retrieve a key via the Web Key Directory
+ * protocol.  On success a new estream with the key is stored at
+ * R_KEY.
+ */
+gpg_error_t
+gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, estream_t *r_key)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct dns_cert_parm_s parm;
+  char *line = NULL;
+
+  memset (&parm, 0, sizeof parm);
+
+  err = open_context (ctrl, &ctx);
+  if (err)
+    return err;
+
+  line = es_bsprintf ("WKD_GET -- %s", name);
+  if (!line)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
+    {
+      err = gpg_error (GPG_ERR_TOO_LARGE);
+      goto leave;
+    }
+
+  parm.memfp = es_fopenmem (0, "rwb");
+  if (!parm.memfp)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = assuan_transact (ctx, line, dns_cert_data_cb, &parm,
+                         NULL, NULL, NULL, &parm);
+  if (err)
+    goto leave;
+
+  if (r_key)
+    {
+      es_rewind (parm.memfp);
+      *r_key = parm.memfp;
+      parm.memfp = NULL;
+    }
+
+ leave:
+  xfree (parm.fpr);
+  xfree (parm.url);
+  es_fclose (parm.memfp);
+  xfree (line);
+  close_context (ctrl, ctx);
+  return err;
+}