gpg-agent: Add restricted connection feature.
authorМирослав Николић <wk@gnupg.org>
Thu, 27 Nov 2014 19:41:37 +0000 (20:41 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 27 Nov 2014 19:41:37 +0000 (20:41 +0100)
* agent/agent.h (opt): Add field extra_socket.
(server_control_s): Add field restricted.
* agent/command.c: Check restricted flag on many commands.
* agent/gpg-agent.c (oExtraSocket): New.
(opts): Add option --extra-socket.
(socket_name_extra): New.
(cleanup): Cleanup that socket name.
(main): Implement oExtraSocket.
(create_socket_name): Add arg homedir and change all callers.
(create_server_socket): Rename arg is_ssh to primary and change
callers.
(start_connection_thread): Take ctrl as arg.
(start_connection_thread_std): New.
(start_connection_thread_extra): New.
(handle_connections): Add arg listen_fd_extra and replace the
connection starting code by parameterized loop.
* common/asshelp.c (start_new_gpg_agent): Detect the use of the
restricted mode and don't fail on sending the pinentry environment.

* common/util.h (GPG_ERR_FORBIDDEN): New.

agent/agent.h
agent/command.c
agent/gpg-agent.c
common/asshelp.c
common/util.h
doc/gpg-agent.texi

index b80c6a0..0c83b27 100644 (file)
@@ -130,6 +130,11 @@ struct
 
   /* This global option enables the ssh-agent subsystem.  */
   int ssh_support;
+
+  /* This global options indicates the use of an extra socket. Note
+     that we use a hack for cleanup handling in gpg-agent.c: If the
+     value is less than 2 the name has not yet been malloced. */
+  int extra_socket;
 } opt;
 
 
@@ -171,6 +176,9 @@ struct server_control_s
     gnupg_fd_t fd;
   } thread_startup;
 
+  /* Flag indicating the connection is run in restricted mode.  */
+  int restricted;
+
   /* Private data of the server (command.c). */
   struct server_local_s *server_local;
 
index 11bfbeb..3e80663 100644 (file)
@@ -502,6 +502,9 @@ cmd_geteventcounter (assuan_context_t ctx, char *line)
 
   (void)line;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   return agent_print_status (ctrl, "EVENTCOUNTER", "%u %u %u",
                              eventcounter.any,
                              eventcounter.key,
@@ -577,10 +580,14 @@ static const char hlp_listtrusted[] =
 static gpg_error_t
 cmd_listtrusted (assuan_context_t ctx, char *line)
 {
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
 
   (void)line;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   rc = agent_listtrusted (ctx);
   return leave_cmd (ctx, rc);
 }
@@ -599,6 +606,9 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
   char fpr[41];
   int flag;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   /* parse the fingerprint value */
   for (p=line,n=0; hexdigitp (p); p++, n++)
     ;
@@ -718,7 +728,12 @@ cmd_setkeydesc (assuan_context_t ctx, char *line)
   plus_to_blank (desc);
 
   xfree (ctrl->server_local->keydesc);
-  ctrl->server_local->keydesc = xtrystrdup (desc);
+
+  if (ctrl->restricted)
+    ctrl->server_local->keydesc = strconcat
+      ("Note: Request from a remote site.\n\n", desc, NULL);
+  else
+    ctrl->server_local->keydesc = xtrystrdup (desc);
   if (!ctrl->server_local->keydesc)
     return out_of_core ();
   return 0;
@@ -928,6 +943,9 @@ cmd_genkey (assuan_context_t ctx, char *line)
   int opt_preset;
   char *p;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   opt_preset = has_option (line, "--preset");
   no_protection = has_option (line, "--no-protection");
   line = skip_options (line);
@@ -974,6 +992,9 @@ cmd_readkey (assuan_context_t ctx, char *line)
   unsigned char grip[20];
   gcry_sexp_t s_pkey = NULL;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   rc = parse_keygrip (ctx, line, grip);
   if (rc)
     return rc; /* Return immediately as this is already an Assuan error code.*/
@@ -1199,6 +1220,9 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
   char hexgrip[41];
   int disabled, ttl, confirm, is_ssh;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   if (has_option (line, "--ssh-list"))
     list_mode = 2;
   else
@@ -1376,6 +1400,9 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
   int opt_repeat = 0;
   char *repeat_errtext = NULL;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   opt_data = has_option (line, "--data");
   opt_check = has_option (line, "--check");
   opt_no_ask = has_option (line, "--no-ask");
@@ -1515,10 +1542,14 @@ static const char hlp_clear_passphrase[] =
 static gpg_error_t
 cmd_clear_passphrase (assuan_context_t ctx, char *line)
 {
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   char *cacheid = NULL;
   char *p;
   int opt_normal;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   opt_normal = has_option (line, "--mode=normal");
   line = skip_options (line);
 
@@ -1557,6 +1588,9 @@ cmd_get_confirmation (assuan_context_t ctx, char *line)
   char *desc = NULL;
   char *p;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   /* parse the stuff */
   for (p=line; *p == ' '; p++)
     ;
@@ -1595,6 +1629,9 @@ cmd_learn (assuan_context_t ctx, char *line)
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   rc = agent_handle_learn (ctrl, has_option (line, "--send")? ctx : NULL);
   return leave_cmd (ctx, rc);
 }
@@ -1621,6 +1658,9 @@ cmd_passwd (assuan_context_t ctx, char *line)
   char *pend;
   int opt_preset;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   opt_preset = has_option (line, "--preset");
   cache_nonce = option_value (line, "--cache-nonce");
   if (cache_nonce)
@@ -1756,6 +1796,7 @@ static const char hlp_preset_passphrase[] =
 static gpg_error_t
 cmd_preset_passphrase (assuan_context_t ctx, char *line)
 {
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
   char *grip_clear = NULL;
   unsigned char *passphrase = NULL;
@@ -1763,6 +1804,9 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
   size_t len;
   int opt_inquire;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   if (!opt.allow_preset_passphrase)
     return set_error (GPG_ERR_NOT_SUPPORTED, "no --allow-preset-passphrase");
 
@@ -1847,6 +1891,9 @@ cmd_scd (assuan_context_t ctx, char *line)
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   rc = divert_generic_cmd (ctrl, line, ctx);
 
   return rc;
@@ -1876,6 +1923,8 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
   gpg_error_t err = 0;
   int clearopt = has_option (line, "--clear");
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
 
   assuan_begin_confidential (ctx);
   if (has_option (line, "--import"))
@@ -1940,6 +1989,9 @@ cmd_import_key (assuan_context_t ctx, char *line)
   char *cache_nonce = NULL;
   char *p;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   if (!ctrl->server_local->import_key)
     {
       err = gpg_error (GPG_ERR_MISSING_KEY);
@@ -2129,6 +2181,9 @@ cmd_export_key (assuan_context_t ctx, char *line)
   char *pend;
   int c;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   openpgp = has_option (line, "--openpgp");
   cache_nonce = option_value (line, "--cache-nonce");
   if (cache_nonce)
@@ -2280,6 +2335,9 @@ cmd_delete_key (assuan_context_t ctx, char *line)
   gpg_error_t err;
   unsigned char grip[20];
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   line = skip_options (line);
 
   err = parse_keygrip (ctx, line, grip);
@@ -2318,6 +2376,9 @@ cmd_keytocard (assuan_context_t ctx, char *line)
   unsigned char *shdkey;
   time_t timestamp;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   force = has_option (line, "--force");
   line = skip_options (line);
 
@@ -2434,6 +2495,8 @@ cmd_keytocard (assuan_context_t ctx, char *line)
  leave:
   return leave_cmd (ctx, err);
 }
+
+
 \f
 static const char hlp_getval[] =
   "GETVAL <key>\n"
@@ -2443,11 +2506,15 @@ static const char hlp_getval[] =
 static gpg_error_t
 cmd_getval (assuan_context_t ctx, char *line)
 {
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc = 0;
   char *key = NULL;
   char *p;
   struct putval_item_s *vl;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   for (p=line; *p == ' '; p++)
     ;
   key = p;
@@ -2498,6 +2565,7 @@ static const char hlp_putval[] =
 static gpg_error_t
 cmd_putval (assuan_context_t ctx, char *line)
 {
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc = 0;
   char *key = NULL;
   char *value = NULL;
@@ -2505,6 +2573,9 @@ cmd_putval (assuan_context_t ctx, char *line)
   char *p;
   struct putval_item_s *vl, *vlprev;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   for (p=line; *p == ' '; p++)
     ;
   key = p;
@@ -2583,6 +2654,9 @@ cmd_updatestartuptty (assuan_context_t ctx, char *line)
 
   (void)line;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   se = session_env_new ();
   if (!se)
     err = gpg_error_from_syserror ();
@@ -2634,6 +2708,9 @@ cmd_killagent (assuan_context_t ctx, char *line)
 
   (void)line;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   ctrl->server_local->stopme = 1;
   assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
   return 0;
@@ -2648,9 +2725,13 @@ static const char hlp_reloadagent[] =
 static gpg_error_t
 cmd_reloadagent (assuan_context_t ctx, char *line)
 {
-  (void)ctx;
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
   (void)line;
 
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
   agent_sighup_action ();
   return 0;
 }
@@ -2672,7 +2753,8 @@ static const char hlp_getinfo[] =
   "  std_session_env - List the standard session environment.\n"
   "  std_startup_env - List the standard startup environment.\n"
   "  cmd_has_option\n"
-  "              - Returns OK if the command CMD implements the option OPT\n.";
+  "              - Returns OK if the command CMD implements the option OPT.\n"
+  "  restricted  - Returns OK if the connection is in restricted mode.\n";
 static gpg_error_t
 cmd_getinfo (assuan_context_t ctx, char *line)
 {
@@ -2684,6 +2766,54 @@ cmd_getinfo (assuan_context_t ctx, char *line)
       const char *s = VERSION;
       rc = assuan_send_data (ctx, s, strlen (s));
     }
+  else if (!strncmp (line, "cmd_has_option", 14)
+           && (line[14] == ' ' || line[14] == '\t' || !line[14]))
+    {
+      char *cmd, *cmdopt;
+      line += 14;
+      while (*line == ' ' || *line == '\t')
+        line++;
+      if (!*line)
+        rc = gpg_error (GPG_ERR_MISSING_VALUE);
+      else
+        {
+          cmd = line;
+          while (*line && (*line != ' ' && *line != '\t'))
+            line++;
+          if (!*line)
+            rc = gpg_error (GPG_ERR_MISSING_VALUE);
+          else
+            {
+              *line++ = 0;
+              while (*line == ' ' || *line == '\t')
+                line++;
+              if (!*line)
+                rc = gpg_error (GPG_ERR_MISSING_VALUE);
+              else
+                {
+                  cmdopt = line;
+                  if (!command_has_option (cmd, cmdopt))
+                    rc = gpg_error (GPG_ERR_GENERAL);
+                }
+            }
+        }
+    }
+  else if (!strcmp (line, "s2k_count"))
+    {
+      char numbuf[50];
+
+      snprintf (numbuf, sizeof numbuf, "%lu", get_standard_s2k_count ());
+      rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else if (!strcmp (line, "restricted"))
+    {
+      rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_GENERAL);
+    }
+  else if (ctrl->restricted)
+    {
+      rc = gpg_error (GPG_ERR_FORBIDDEN);
+    }
+  /* All sub-commands below are not allowed in restricted mode.  */
   else if (!strcmp (line, "pid"))
     {
       char numbuf[50];
@@ -2713,13 +2843,6 @@ cmd_getinfo (assuan_context_t ctx, char *line)
     {
       rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_GENERAL);
     }
-  else if (!strcmp (line, "s2k_count"))
-    {
-      char numbuf[50];
-
-      snprintf (numbuf, sizeof numbuf, "%lu", get_standard_s2k_count ());
-      rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
-    }
   else if (!strcmp (line, "std_session_env")
            || !strcmp (line, "std_startup_env"))
     {
@@ -2748,38 +2871,6 @@ cmd_getinfo (assuan_context_t ctx, char *line)
             }
         }
     }
-  else if (!strncmp (line, "cmd_has_option", 14)
-           && (line[14] == ' ' || line[14] == '\t' || !line[14]))
-    {
-      char *cmd, *cmdopt;
-      line += 14;
-      while (*line == ' ' || *line == '\t')
-        line++;
-      if (!*line)
-        rc = gpg_error (GPG_ERR_MISSING_VALUE);
-      else
-        {
-          cmd = line;
-          while (*line && (*line != ' ' && *line != '\t'))
-            line++;
-          if (!*line)
-            rc = gpg_error (GPG_ERR_MISSING_VALUE);
-          else
-            {
-              *line++ = 0;
-              while (*line == ' ' || *line == '\t')
-                line++;
-              if (!*line)
-                rc = gpg_error (GPG_ERR_MISSING_VALUE);
-              else
-                {
-                  cmdopt = line;
-                  if (!command_has_option (cmd, cmdopt))
-                    rc = gpg_error (GPG_ERR_GENERAL);
-                }
-            }
-        }
-    }
   else
     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
   return rc;
@@ -2802,6 +2893,11 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
       ctrl->server_local->allow_fully_canceled =
         gnupg_compare_version (value, "2.1.0");
     }
+  else if (ctrl->restricted)
+    {
+      err = gpg_error (GPG_ERR_FORBIDDEN);
+    }
+  /* All options below are not allowed in restricted mode.  */
   else if (!strcmp (key, "putenv"))
     {
       /* Change the session's environment to be used for the
index 23d6ee2..e001ad1 100644 (file)
@@ -111,6 +111,7 @@ enum cmd_and_opt_values
   oEnablePassphraseHistory,
   oUseStandardSocket,
   oNoUseStandardSocket,
+  oExtraSocket,
   oFakedSystemTime,
 
   oIgnoreCacheForSigning,
@@ -209,6 +210,7 @@ static ARGPARSE_OPTS opts[] = {
                 /* */           "@"
 #endif
                 ),
+  ARGPARSE_s_s (oExtraSocket, "extra-socket", "@"),
 
   /* Dummy options for backward compatibility.  */
   ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
@@ -280,12 +282,16 @@ static int maybe_setuid = 1;
 /* Name of the communication socket used for native gpg-agent requests.  */
 static char *socket_name;
 
+/* Name of the optional extra socket used for native gpg-agent requests.  */
+static char *socket_name_extra;
+
 /* Name of the communication socket used for ssh-agent-emulation.  */
 static char *socket_name_ssh;
 
 /* We need to keep track of the server's nonces (these are dummies for
    POSIX systems). */
 static assuan_sock_nonce_t socket_nonce;
+static assuan_sock_nonce_t socket_nonce_extra;
 static assuan_sock_nonce_t socket_nonce_ssh;
 
 
@@ -320,8 +326,8 @@ static int active_connections;
    Local prototypes.
  */
 
-static char *create_socket_name (char *standard_name);
-static gnupg_fd_t create_server_socket (char *name, int is_ssh,
+static char *create_socket_name (char *standard_name, int with_homedir);
+static gnupg_fd_t create_server_socket (char *name, int primary,
                                         assuan_sock_nonce_t *nonce);
 static void create_directories (void);
 
@@ -329,6 +335,7 @@ static void agent_init_default_ctrl (ctrl_t ctrl);
 static void agent_deinit_default_ctrl (ctrl_t ctrl);
 
 static void handle_connections (gnupg_fd_t listen_fd,
+                                gnupg_fd_t listen_fd_extra,
                                 gnupg_fd_t listen_fd_ssh);
 static void check_own_socket (void);
 static int check_for_running_agent (int silent);
@@ -498,6 +505,8 @@ cleanup (void)
   done = 1;
   deinitialize_module_cache ();
   remove_socket (socket_name);
+  if (opt.extra_socket > 1)
+    remove_socket (socket_name_extra);
   remove_socket (socket_name_ssh);
 }
 
@@ -860,6 +869,11 @@ main (int argc, char **argv )
 #        endif
           break;
 
+        case oExtraSocket:
+          opt.extra_socket = 1;  /* (1 = points into argv)  */
+          socket_name_extra = pargs.r.ret_str;
+          break;
+
         case oDebugQuickRandom:
           /* Only used by the first stage command line parser.  */
           break;
@@ -1067,7 +1081,8 @@ main (int argc, char **argv )
   else
     { /* Regular server mode */
       gnupg_fd_t fd;
-      gnupg_fd_t fd_ssh;
+      gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
+      gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
       pid_t pid;
 
       /* Remove the DISPLAY variable so that a pinentry does not
@@ -1081,17 +1096,23 @@ main (int argc, char **argv )
         gnupg_unsetenv ("DISPLAY");
 #endif
 
-
       /* Create the sockets.  */
-      socket_name = create_socket_name (GPG_AGENT_SOCK_NAME);
-      fd = create_server_socket (socket_name, 0, &socket_nonce);
+      socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
+      fd = create_server_socket (socket_name, 1, &socket_nonce);
+
+      if (opt.extra_socket)
+        {
+          socket_name_extra = create_socket_name (socket_name_extra, 0);
+          opt.extra_socket = 2; /* Indicate that it has been malloced.  */
+          fd_extra = create_server_socket (socket_name_extra, 0,
+                                           &socket_nonce_extra);
+        }
+
       if (opt.ssh_support)
         {
-          socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME);
-          fd_ssh = create_server_socket (socket_name_ssh, 1, &socket_nonce_ssh);
+          socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
+          fd_ssh = create_server_socket (socket_name_ssh, 0, &socket_nonce_ssh);
         }
-      else
-       fd_ssh = GNUPG_INVALID_FD;
 
       /* If we are going to exec a program in the parent, we record
          the PID, so that the child may check whether the program is
@@ -1154,6 +1175,8 @@ main (int argc, char **argv )
 
           *socket_name = 0; /* Don't let cleanup() remove the socket -
                                the child should do this from now on */
+         if (opt.extra_socket)
+           *socket_name_extra = 0;
          if (opt.ssh_support)
            *socket_name_ssh = 0;
 
@@ -1264,7 +1287,7 @@ main (int argc, char **argv )
 #endif /*!HAVE_W32_SYSTEM*/
 
       log_info ("%s %s started\n", strusage(11), strusage(13) );
-      handle_connections (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD);
+      handle_connections (fd, fd_extra, fd_ssh);
       assuan_sock_close (fd);
     }
 
@@ -1304,7 +1327,7 @@ agent_exit (int rc)
    structure usually identified by an argument named CTRL.  This
    function is called immediately after allocating the control
    structure.  Its purpose is to setup the default values for that
-   structure.  */
+   structure.  Note that some values may have already been set.  */
 static void
 agent_init_default_ctrl (ctrl_t ctrl)
 {
@@ -1463,11 +1486,14 @@ get_agent_scd_notify_event (void)
    Pointer to an allocated string with the absolute name of the socket
    used.  */
 static char *
-create_socket_name (char *standard_name)
+create_socket_name (char *standard_name, int with_homedir)
 {
   char *name;
 
-  name = make_filename (opt.homedir, standard_name, NULL);
+  if (with_homedir)
+    name = make_filename (opt.homedir, standard_name, NULL);
+  else
+    name = make_filename (standard_name, NULL);
   if (strchr (name, PATHSEP_C))
     {
       log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
@@ -1484,11 +1510,11 @@ create_socket_name (char *standard_name)
 
 
 /* Create a Unix domain socket with NAME.  Returns the file descriptor
-   or terminates the process in case of an error.  Not that this
-   function needs to be used for the regular socket first and only
-   then for the ssh socket.  */
+   or terminates the process in case of an error.  Note that this
+   function needs to be used for the regular socket first (indicated
+   by PRIMARY) and only then for the extra and the ssh sockets.  */
 static gnupg_fd_t
-create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce)
+create_server_socket (char *name, int primary, assuan_sock_nonce_t *nonce)
 {
   struct sockaddr_un *serv_addr;
   socklen_t len;
@@ -1531,7 +1557,7 @@ create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce)
          know the new Assuan socket, the Assuan server and thus the
          ssh-agent server is not yet operational.  This would lead to
          a hang.  */
-      if (!is_ssh && !check_for_running_agent (1))
+      if (primary && !check_for_running_agent (1))
         {
           log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX);
           log_set_file (NULL);
@@ -1980,12 +2006,9 @@ putty_message_thread (void *arg)
 #endif /*HAVE_W32_SYSTEM*/
 
 
-/* This is the standard connection thread's main function.  */
 static void *
-start_connection_thread (void *arg)
+start_connection_thread (ctrl_t ctrl)
 {
-  ctrl_t ctrl = arg;
-
   if (check_nonce (ctrl, &socket_nonce))
     {
       log_error ("handler 0x%lx nonce check FAILED\n",
@@ -2009,6 +2032,27 @@ start_connection_thread (void *arg)
 }
 
 
+/* This is the standard connection thread's main function.  */
+static void *
+start_connection_thread_std (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  return start_connection_thread (ctrl);
+}
+
+
+/* This is the extra socket connection thread's main function.  */
+static void *
+start_connection_thread_extra (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  ctrl->restricted = 1;
+  return start_connection_thread (ctrl);
+}
+
+
 /* This is the ssh connection thread's main function.  */
 static void *
 start_connection_thread_ssh (void *arg)
@@ -2037,7 +2081,9 @@ start_connection_thread_ssh (void *arg)
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  */
 static void
-handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
+handle_connections (gnupg_fd_t listen_fd,
+                    gnupg_fd_t listen_fd_extra,
+                    gnupg_fd_t listen_fd_ssh)
 {
   npth_attr_t tattr;
   struct sockaddr_un paddr;
@@ -2054,6 +2100,16 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
   HANDLE events[2];
   unsigned int events_set;
 #endif
+  struct {
+    const char *name;
+    void *(*func) (void *arg);
+    gnupg_fd_t l_fd;
+  } listentbl[] = {
+    { "std",  start_connection_thread_std   },
+    { "extra",start_connection_thread_extra },
+    { "ssh",  start_connection_thread_ssh   }
+  };
+
 
   ret = npth_attr_init(&tattr);
   if (ret)
@@ -2103,6 +2159,12 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
   FD_ZERO (&fdset);
   FD_SET (FD2INT (listen_fd), &fdset);
   nfd = FD2INT (listen_fd);
+  if (listen_fd_extra != GNUPG_INVALID_FD)
+    {
+      FD_SET ( FD2INT(listen_fd_extra), &fdset);
+      if (FD2INT (listen_fd_extra) > nfd)
+        nfd = FD2INT (listen_fd_extra);
+    }
   if (listen_fd_ssh != GNUPG_INVALID_FD)
     {
       FD_SET ( FD2INT(listen_fd_ssh), &fdset);
@@ -2110,6 +2172,10 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
         nfd = FD2INT (listen_fd_ssh);
     }
 
+  listentbl[0].l_fd = listen_fd;
+  listentbl[1].l_fd = listen_fd_extra;
+  listentbl[2].l_fd = listen_fd_ssh;
+
   npth_clock_gettime (&abstime);
   abstime.tv_sec += TIMERTICK_INTERVAL;
 
@@ -2172,92 +2238,56 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
           next timeout.  */
        continue;
 
-      if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
-       {
+      if (!shutdown_pending)
+        {
+          int idx;
           ctrl_t ctrl;
+          npth_t thread;
 
-          plen = sizeof paddr;
-         fd = INT2FD (npth_accept (FD2INT(listen_fd),
-                                   (struct sockaddr *)&paddr, &plen));
-         if (fd == GNUPG_INVALID_FD)
-           {
-             log_error ("accept failed: %s\n", strerror (errno));
-           }
-          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
-            {
-              log_error ("error allocating connection control data: %s\n",
-                         strerror (errno) );
-              assuan_sock_close (fd);
-            }
-          else if ( !(ctrl->session_env = session_env_new ()) )
+          for (idx=0; idx < DIM(listentbl); idx++)
             {
-              log_error ("error allocating session environment block: %s\n",
-                         strerror (errno) );
-              xfree (ctrl);
-              assuan_sock_close (fd);
-            }
-          else
-            {
-             npth_t thread;
-
-              ctrl->thread_startup.fd = fd;
-             ret = npth_create (&thread, &tattr,
-                                 start_connection_thread, ctrl);
-              if (ret)
+              if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
+                continue;
+              if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
+                continue;
+
+              plen = sizeof paddr;
+              fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd),
+                                        (struct sockaddr *)&paddr, &plen));
+              if (fd == GNUPG_INVALID_FD)
                 {
-                  log_error ("error spawning connection handler: %s\n",
-                            strerror (ret));
-                  assuan_sock_close (fd);
-                  xfree (ctrl);
+                  log_error ("accept failed for %s: %s\n",
+                             listentbl[idx].name, strerror (errno));
                 }
-
-            }
-          fd = GNUPG_INVALID_FD;
-       }
-
-      if (!shutdown_pending && listen_fd_ssh != GNUPG_INVALID_FD
-          && FD_ISSET ( FD2INT (listen_fd_ssh), &read_fdset))
-       {
-          ctrl_t ctrl;
-
-          plen = sizeof paddr;
-         fd = INT2FD(npth_accept (FD2INT(listen_fd_ssh),
-                                  (struct sockaddr *)&paddr, &plen));
-         if (fd == GNUPG_INVALID_FD)
-           {
-             log_error ("accept failed for ssh: %s\n", strerror (errno));
-           }
-          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
-            {
-              log_error ("error allocating connection control data: %s\n",
-                         strerror (errno) );
-              assuan_sock_close (fd);
-            }
-          else if ( !(ctrl->session_env = session_env_new ()) )
-            {
-              log_error ("error allocating session environment block: %s\n",
-                         strerror (errno) );
-              xfree (ctrl);
-              assuan_sock_close (fd);
-            }
-          else
-            {
-             npth_t thread;
-
-              agent_init_default_ctrl (ctrl);
-              ctrl->thread_startup.fd = fd;
-              ret = npth_create (&thread, &tattr,
-                                 start_connection_thread_ssh, ctrl);
-             if (ret)
+              else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
                 {
-                  log_error ("error spawning ssh connection handler: %s\n",
-                            strerror (ret));
+                  log_error ("error allocating connection data for %s: %s\n",
+                             listentbl[idx].name, strerror (errno) );
                   assuan_sock_close (fd);
+                }
+              else if ( !(ctrl->session_env = session_env_new ()))
+                {
+                  log_error ("error allocating session env block for %s: %s\n",
+                             listentbl[idx].name, strerror (errno) );
                   xfree (ctrl);
+                  assuan_sock_close (fd);
+                }
+              else
+                {
+                  ctrl->thread_startup.fd = fd;
+                  ret = npth_create (&thread, &tattr,
+                                     listentbl[idx].func, ctrl);
+                  if (ret)
+                    {
+                      log_error ("error spawning connection handler for %s:"
+                                 " %s\n", listentbl[idx].name, strerror (ret));
+                      assuan_sock_close (fd);
+                      xfree (ctrl);
+                    }
                 }
+              fd = GNUPG_INVALID_FD;
             }
-          fd = GNUPG_INVALID_FD;
-       }
+        }
     }
 
   cleanup ();
index e675fda..51ef172 100644 (file)
@@ -504,9 +504,23 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
   err = assuan_transact (ctx, "RESET",
                          NULL, NULL, NULL, NULL, NULL, NULL);
   if (!err)
-    err = send_pinentry_environment (ctx, errsource,
-                                     opt_lc_ctype, opt_lc_messages,
-                                     session_env);
+    {
+      err = send_pinentry_environment (ctx, errsource,
+                                       opt_lc_ctype, opt_lc_messages,
+                                       session_env);
+      if (gpg_err_code (err) == GPG_ERR_FORBIDDEN
+          && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT)
+        {
+          /* Check whether we are in restricted mode.  */
+          if (!assuan_transact (ctx, "GETINFO restricted",
+                                NULL, NULL, NULL, NULL, NULL, NULL))
+            {
+              if (verbose)
+                log_info (_("connection to agent is in restricted mode\n"));
+              err = 0;
+            }
+        }
+    }
   if (err)
     {
       assuan_release (ctx);
index dd5fdb1..a6f8606 100644 (file)
 #include <errno.h>  /* We need errno.  */
 #include <gpg-error.h> /* We need gpg_error_t and estream. */
 
+/* These error codes are used but not defined in the required
+   libgpg-error version.  Define them here. */
+#if GPG_ERROR_VERSION_NUMBER < 0x011200  /* 1.18 */
+# define GPG_ERR_FORBIDDEN    251
+#endif
+
 
 /* Hash function used with libksba. */
 #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
index a4079d7..7523043 100644 (file)
@@ -532,6 +532,19 @@ Ignore requests to change the current @code{tty} or X window system's
 @code{DISPLAY} variable respectively.  This is useful to lock the
 pinentry to pop up at the @code{tty} or display you started the agent.
 
+
+@anchor{option --extra-socket}
+@item --extra-socket @var{name}
+@opindex extra-socket
+Also listen on native gpg-agent connections on the given socket.  The
+intended use for this extra socket is to setup a Unix domain socket
+forwarding from a remote machine to this socket on the local machine.
+A @command{gpg} running on the remote machine may then connect to the
+local gpg-agent and use its private keys.  This allows to decrypt or
+sign data on a remote machine without exposing the private keys to the
+remote machine.
+
+
 @anchor{option --enable-ssh-support}
 @item --enable-ssh-support
 @opindex enable-ssh-support