diff options
author | Мирослав Николић <wk@gnupg.org> | 2014-11-27 20:41:37 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2014-11-27 20:41:37 +0100 |
commit | f173cdcdfbfd083b035516a406c2c754f38a0ace (patch) | |
tree | b1f162fcb7e39ade104379129f6731aacdce2344 | |
parent | agent: Make auditing of the option list easier. (diff) | |
download | gnupg2-f173cdcdfbfd083b035516a406c2c754f38a0ace.tar.xz gnupg2-f173cdcdfbfd083b035516a406c2c754f38a0ace.zip |
gpg-agent: Add restricted connection feature.
* 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.
-rw-r--r-- | agent/agent.h | 8 | ||||
-rw-r--r-- | agent/command.c | 180 | ||||
-rw-r--r-- | agent/gpg-agent.c | 230 | ||||
-rw-r--r-- | common/asshelp.c | 20 | ||||
-rw-r--r-- | common/util.h | 6 | ||||
-rw-r--r-- | doc/gpg-agent.texi | 13 |
6 files changed, 312 insertions, 145 deletions
diff --git a/agent/agent.h b/agent/agent.h index b80c6a05c..0c83b274d 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -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; diff --git a/agent/command.c b/agent/command.c index 11bfbeb67..3e8066381 100644 --- a/agent/command.c +++ b/agent/command.c @@ -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); } + + 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 diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 23d6ee2b2..e001ad12e 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -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 (); diff --git a/common/asshelp.c b/common/asshelp.c index e675fdad8..51ef17227 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -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); diff --git a/common/util.h b/common/util.h index dd5fdb14c..a6f86069a 100644 --- a/common/util.h +++ b/common/util.h @@ -35,6 +35,12 @@ #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) diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index a4079d7ed..7523043bb 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -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 |