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.
This commit is contained in:
Мирослав Николић 2014-11-27 20:41:37 +01:00 committed by Werner Koch
parent ccee34736b
commit f173cdcdfb
6 changed files with 337 additions and 170 deletions

View 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;

View 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);
}
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,70 +2766,6 @@ cmd_getinfo (assuan_context_t ctx, char *line)
const char *s = VERSION;
rc = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
char numbuf[50];
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else if (!strcmp (line, "socket_name"))
{
const char *s = get_agent_socket_name ();
if (s)
rc = assuan_send_data (ctx, s, strlen (s));
else
rc = gpg_error (GPG_ERR_NO_DATA);
}
else if (!strcmp (line, "ssh_socket_name"))
{
const char *s = get_agent_ssh_socket_name ();
if (s)
rc = assuan_send_data (ctx, s, strlen (s));
else
rc = gpg_error (GPG_ERR_NO_DATA);
}
else if (!strcmp (line, "scd_running"))
{
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"))
{
int iterator;
const char *name, *value;
char *string;
iterator = 0;
while ((name = session_env_list_stdenvnames (&iterator, NULL)))
{
value = session_env_getenv_or_default
(line[5] == 't'? opt.startup_env:ctrl->session_env, name, NULL);
if (value)
{
string = xtryasprintf ("%s=%s", name, value);
if (!string)
rc = gpg_error_from_syserror ();
else
{
rc = assuan_send_data (ctx, string, strlen (string)+1);
if (!rc)
rc = assuan_send_data (ctx, NULL, 0);
}
if (rc)
break;
}
}
}
else if (!strncmp (line, "cmd_has_option", 14)
&& (line[14] == ' ' || line[14] == '\t' || !line[14]))
{
@ -2780,6 +2798,79 @@ cmd_getinfo (assuan_context_t ctx, char *line)
}
}
}
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];
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else if (!strcmp (line, "socket_name"))
{
const char *s = get_agent_socket_name ();
if (s)
rc = assuan_send_data (ctx, s, strlen (s));
else
rc = gpg_error (GPG_ERR_NO_DATA);
}
else if (!strcmp (line, "ssh_socket_name"))
{
const char *s = get_agent_ssh_socket_name ();
if (s)
rc = assuan_send_data (ctx, s, strlen (s));
else
rc = gpg_error (GPG_ERR_NO_DATA);
}
else if (!strcmp (line, "scd_running"))
{
rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_GENERAL);
}
else if (!strcmp (line, "std_session_env")
|| !strcmp (line, "std_startup_env"))
{
int iterator;
const char *name, *value;
char *string;
iterator = 0;
while ((name = session_env_list_stdenvnames (&iterator, NULL)))
{
value = session_env_getenv_or_default
(line[5] == 't'? opt.startup_env:ctrl->session_env, name, NULL);
if (value)
{
string = xtryasprintf ("%s=%s", name, value);
if (!string)
rc = gpg_error_from_syserror ();
else
{
rc = assuan_send_data (ctx, string, strlen (string)+1);
if (!rc)
rc = assuan_send_data (ctx, NULL, 0);
}
if (rc)
break;
}
}
}
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

View 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)) )
for (idx=0; idx < DIM(listentbl); idx++)
{
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;
if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
continue;
if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
continue;
ctrl->thread_startup.fd = fd;
ret = npth_create (&thread, &tattr,
start_connection_thread, ctrl);
if (ret)
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);
xfree (ctrl);
}
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 ();

View 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);

View File

@ -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)

View 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