Imported Upstream version 2.1.19
[platform/upstream/gpg2.git] / g10 / call-dirmngr.c
index 83af0be..2f2ba98 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -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) < 0)
+    {
+      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;
 
@@ -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)
@@ -341,7 +374,8 @@ ks_status_cb (void *opaque, const char *line)
 {
   struct ks_status_parm_s *parm = opaque;
   gpg_error_t err = 0;
-  const char *s;
+  const char *s, *s2;
+  const char *warn;
 
   if ((s = has_leading_keyword (line, parm->keyword? parm->keyword : "SOURCE")))
     {
@@ -352,6 +386,29 @@ ks_status_cb (void *opaque, const char *line)
             err = gpg_error_from_syserror ();
         }
     }
+  else if ((s = has_leading_keyword (line, "WARNING")))
+    {
+      if ((s2 = has_leading_keyword (s, "tor_not_running")))
+        warn = _("Tor is not running");
+      else if ((s2 = has_leading_keyword (s, "tor_config_problem")))
+        warn = _("Tor is not properly configured");
+      else
+        warn = NULL;
+
+      if (warn)
+        {
+          log_info (_("WARNING: %s\n"), warn);
+          if (s2)
+            {
+              while (*s2 && !spacep (s2))
+                s2++;
+              while (*s2 && spacep (s2))
+                s2++;
+              if (*s2)
+                print_further_info ("%s", s2);
+            }
+        }
+    }
 
   return err;
 }
@@ -369,7 +426,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 +443,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:
@@ -563,6 +624,8 @@ ks_get_data_cb (void *opaque, const void *data, size_t datalen)
    don't need to escape the patterns before sending them to the
    server.
 
+   If QUICK is set the dirmngr is advised to use a shorter timeout.
+
    If R_SOURCE is not NULL the source of the data is stored as a
    malloced string there.  If a source is not known NULL is stored.
 
@@ -572,7 +635,7 @@ ks_get_data_cb (void *opaque, const void *data, size_t datalen)
    are able to ask for (1000-10-1)/(2+8+1) = 90 keys at once.  */
 gpg_error_t
 gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern,
-                    keyserver_spec_t override_keyserver,
+                    keyserver_spec_t override_keyserver, int quick,
                     estream_t *r_fp, char **r_source)
 {
   gpg_error_t err;
@@ -618,7 +681,7 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern,
 
   /* Lump all patterns into one string.  */
   init_membuf (&mb, 1024);
-  put_membuf_str (&mb, "KS_GET --");
+  put_membuf_str (&mb, quick? "KS_GET --quick --" : "KS_GET --");
   for (idx=0; pattern[idx]; idx++)
     {
       put_membuf (&mb, " ", 1); /* Append Delimiter.  */
@@ -765,7 +828,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 +1088,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 +1143,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 +1311,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.  If QUICK is set the dirmngr is advised to use a shorter
+ * timeout.  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, int quick, 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 -- %s", quick?" --quick":"", 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;
+}