Get GPG Agent's socket directly from the agent.
authorDamien Goutte-Gattat <dgouttegattat@incenp.org>
Mon, 16 Jan 2017 10:49:18 +0000 (11:49 +0100)
committerDamien Goutte-Gattat <dgouttegattat@incenp.org>
Wed, 5 Jul 2017 19:40:55 +0000 (21:40 +0200)
* src/agent.c (agent_connect): Call gpg-connect-agent to get
the socket for a running agent.
* src/get-path.c (get_gpg_connect_agent_path): New function.
* src/support.h (get_gpg_connect_agent_path): New prototype.
* configure.ac: New option --with-gpg-connect-agent-path.
--

This patch replaces all the logic needed to find the socket for
a running GnuPG Agent by a single call to gpg-connect-agent.
This will ensure we will always be able to find the agent,
without having to duplicate the logic already implemented in
GnuPG. Gpg-connect-agent will also take care of starting the
agent if it's not already running.

GnuPG-bug-id: 3195
Signed-off-by: Damien Goutte-Gattat <dgouttegattat@incenp.org>
configure.ac
src/agent.c
src/get-path.c
src/support.h

index 1e4137d..8567a3a 100644 (file)
@@ -232,12 +232,14 @@ AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 
 GPGSM_DEFAULT=no
 GPG_AGENT_DEFAULT=no
+GPG_CONNECT_AGENT_DEFAULT=no
 have_w32_system=no
 case "${host}" in
     *-mingw32*)
         # special stuff for Windoze NT
        GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe'
         GPG_AGENT_DEFAULT='c:\\gnupg\\gpg-agent.exe'
+        GPG_CONNECT_AGENT_DEFAULT='c:\\gnupg\\gpg-connect-agent.exe'
        have_w32_system=yes
         ;;
     *)
@@ -406,6 +408,41 @@ else
 fi
 AM_CONDITIONAL(HAVE_GPG_AGENT, test "$GPG_AGENT" != "no")
 
+# GPG_CONNECT_AGENT
+NO_OVERRIDE=no
+AC_ARG_WITH(gpg-connect-agent,
+            AC_HELP_STRING([--with-gpg-connect-agent=PATH],
+                           [use gpg-connect-agent binary at PATH]),
+            GPG_CONNECT_AGENT=$withval, NO_OVERRIDE=yes)
+if test "$NO_OVERRIDE" = "yes" || test "$GPG_CONNECT_AGENT" = "yes"; then
+  GPG_CONNECT_AGENT=
+  NO_OVERRIDE=yes
+  if test "$cross_compiling" != "yes"; then
+    AC_PATH_PROG(GPG_CONNECT_AGENT, gpg-connect-agent)
+  fi
+  if test -z "$GPG_CONNECT_AGENT"; then
+    GPG_CONNECT_AGENT="$GPG_CONNECT_AGENT_DEFAULT"
+  fi
+fi
+if test "$GPG_CONNECT_AGENT" = no; then
+  if test "$NO_OVERRIDE" = "yes"; then
+    if test "$cross_compiling" != "yes"; then
+      AC_MSG_WARN([
+***
+*** Could not find gpg-connect-agent, use --with-gpg-connect-agent=PATH to enable it
+***])
+    else
+      AC_MSG_WARN([
+***
+*** Can not determine path to gpg-connect-agent when cross-compiling, use --with-gpg-connect-agent=PATH
+***])
+    fi
+  fi
+else
+  AC_DEFINE_UNQUOTED(GPG_CONNECT_AGENT_PATH, "$GPG_CONNECT_AGENT",
+                     [Path to the GPG_CONNECT_AGENT binary.])
+fi
+
 
 # Checks for header files.
 AC_HEADER_STDC
index 75d4933..6ee106c 100644 (file)
@@ -233,151 +233,59 @@ spawn_process_detached (const char *pgmname, const char *argv[])
 static gpg_error_t
 agent_connect (assuan_context_t *ctx_r)
 {
-  /* If we ever failed to connect via a socket we will force the use
-     of the pipe based server for the lifetime of the process.  */
-  static int force_pipe_server = 0;
-
   gpg_error_t err = 0;
-  char *infostr;
-  char *ptr;
   assuan_context_t ctx = NULL;
+  char buffer[255];
+  FILE *p;
 
-  err = assuan_new (&ctx);
-  if (err)
-    return err;
-
- restart:
-
-  infostr = force_pipe_server ? NULL : getenv ("GPG_AGENT_INFO");
-  if (!infostr || !*infostr)
-    {
-      char *sockname;
-
-      /* First check whether we can connect at the standard
-         socket.  */
-      sockname = make_filename (default_homedir (), "S.gpg-agent", NULL);
-      if (! sockname)
-       return gpg_error_from_errno (errno);
-
-      err = assuan_socket_connect (ctx, sockname, 0, 0);
-      if (err)
-        {
-         const char *agent_program;
-
-          /* With no success start a new server.  */
-         DEBUG (DBG_INFO, "no running GPG agent at %s, starting one\n",
-                sockname);
-
-          agent_program = get_gpg_agent_path ();
-
+  /* Use gpg-connect-agent to obtain the socket name
+   * directly from the agent itself. */
+  snprintf (buffer, sizeof buffer, "%s 'GETINFO socket_name' /bye",
+            get_gpg_connect_agent_path ());
 #ifdef HAVE_W32_SYSTEM
-          {
-            /* Under Windows we start the server in daemon mode.  This
-               is because the default is to use the standard socket
-               and thus there is no need for the GPG_AGENT_INFO
-               envvar.  This is possible as we don't have a real unix
-               domain socket but use a plain file and thus there is no
-               need to care about non-local file systems. */
-            const char *argv[3];
-
-            argv[0] = "--daemon";
-            argv[1] = "--use-standard-socket";
-            argv[2] = NULL;
-
-            err = spawn_process_detached (agent_program, argv);
-            if (err)
-              DEBUG (DBG_CRIT, "failed to start agent `%s': %s\n",
-                    agent_program, gpg_strerror (err));
-            else
-              {
-                /* Give the agent some time to prepare itself. */
-                Sleep (3 * 1000);
-                /* Now try again to connect the agent.  */
-                err = assuan_socket_connect (ctx_r, sockname, 0, 0);
-              }
-          }
-#else /*!HAVE_W32_SYSTEM*/
-          {
-            const char *pgmname;
-            const char *argv[3];
-            int no_close_list[3];
-            int i;
-
-            if ( !(pgmname = strrchr (agent_program, '/')))
-              pgmname = agent_program;
-            else
-              pgmname++;
-
-            argv[0] = pgmname;
-            argv[1] = "--server";
-            argv[2] = NULL;
-
-            i=0;
-            no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
-            no_close_list[i] = -1;
-
-            /* Connect to the agent and perform initial handshaking. */
-            err = assuan_pipe_connect (ctx, agent_program, argv,
-                                      no_close_list, NULL, NULL, 0);
-          }
-#endif /*!HAVE_W32_SYSTEM*/
-        }
-      free (sockname);
-    }
-  else
+  p = _popen (buffer, "r");
+#else
+  p = popen (buffer, "r");
+#endif
+  if (p)
     {
-      int pid;
-      int protocol_version;
+      int ret;
 
-      infostr = strdup (infostr);
-      if (!infostr)
-       return gpg_error_from_errno (errno);
-
-      if (!(ptr = strchr (infostr, PATHSEP_C)) || ptr == infostr)
-       {
-         DEBUG (DBG_CRIT, "malformed GPG_AGENT_INFO environment variable");
-         free (infostr);
-         force_pipe_server = 1;
-         goto restart;
-       }
-
-      *(ptr++) = 0;
-      pid = atoi (ptr);
-      while (*ptr && *ptr != PATHSEP_C)
-       ptr++;
-      protocol_version = *ptr ? atoi (ptr + 1) : 0;
-      if (protocol_version != 1)
-       {
-         DEBUG (DBG_CRIT, "GPG agent protocol version '%d' not supported",
-                protocol_version);
-         free (infostr);
-         force_pipe_server = 1;
-         goto restart;
-       }
+      ret = fscanf (p, "D %254s\nOK\n", buffer);
+      if (ret == EOF)       /* I/O error? */
+        err = gpg_error_from_errno (errno);
+      else if (ret != 1)    /* Unexpected reply */
+        err = gpg_error (GPG_ERR_NO_AGENT);
 
-      err = assuan_socket_connect (ctx, infostr, pid, 0);
-      free (infostr);
-      if (err)
-       {
-         DEBUG (DBG_CRIT, "cannot connect to GPG agent: %s", gpg_strerror (err));
-         force_pipe_server = 1;
-         goto restart;
-       }
+      pclose (p);
     }
+  else
+    err = gpg_error_from_errno (errno);
 
-  if (err)
+  /* Then connect to the socket we got. */
+  if (!err)
     {
-      assuan_release (ctx);
-      DEBUG (DBG_CRIT, "cannot connect to GPG agent: %s", gpg_strerror (err));
-      return gpg_error (GPG_ERR_NO_AGENT);
+      err = assuan_new (&ctx);
+      if (!err)
+        {
+          err = assuan_socket_connect (ctx, buffer, 0, 0);
+          if (!err)
+            {
+              *ctx_r = ctx;
+              if (_scute_debug_flags & DBG_ASSUAN)
+                assuan_set_log_stream (*ctx_r, _scute_debug_stream);
+            }
+          else
+            assuan_release (ctx);
+        }
     }
 
-  if (_scute_debug_flags & DBG_ASSUAN)
-    assuan_set_log_stream (*ctx_r, _scute_debug_stream);
-
-  *ctx_r = ctx;
+  /* We do not try any harder. If gpg-connect-agent somehow failed
+   * to give us a suitable socket, we probably cannot do better. */
+  if (err)
+    DEBUG (DBG_CRIT, "cannot connect to GPG agent: %s", gpg_strerror (err));
 
-  return 0;
+  return err;
 }
 
 
index 0abd863..cb0a136 100644 (file)
@@ -335,6 +335,23 @@ get_gpg_agent_path (void)
 }
 
 
+const char *
+get_gpg_connect_agent_path (void)
+{
+  static const char *pgmname;
+
+#ifdef HAVE_W32_SYSTEM
+  if (!pgmname)
+    pgmname = find_program_in_inst_dir ("gpg-connect-agent.exe");
+  if (!pgmname)
+    pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpg-connect-agent.exe");
+#endif
+  if (!pgmname)
+    pgmname = GPG_CONNECT_AGENT_PATH;
+  return pgmname;
+}
+
+
 \f
 /* Home directory.  */
 
index 3356224..739d124 100644 (file)
@@ -85,6 +85,7 @@ stpcpy (char *a, const char *b)
 
 const char *get_gpgsm_path (void);
 const char *get_gpg_agent_path (void);
+const char *get_gpg_connect_agent_path (void);
 
 /* Set up the default home directory.  The usual --homedir option
    should be parsed later. */