1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-31 22:18:03 +02:00

agent: Implement socket redirection.

* agent/gpg-agent.c (ENAMETOOLONG): New.
(redir_socket_name, redir_socket_name_extra)
(redir_socket_name_ssh): New.
(remove_socket): Take care of the redir names.
(main): Pass the redir names to create_server_socket.
(create_socket_name): Remove length check - that is anyway done later.
(create_server_socket): Add arg r_redir_name and implement redirection
if Libassuan is at least 2.14.
This commit is contained in:
Werner Koch 2014-11-28 21:34:35 +01:00
parent e59b1cc747
commit e1f515b19c

View File

@ -129,6 +129,10 @@ enum cmd_and_opt_values
}; };
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
static ARGPARSE_OPTS opts[] = { static ARGPARSE_OPTS opts[] = {
@ -279,14 +283,19 @@ static int disable_check_own_socket;
/* It is possible that we are currently running under setuid permissions */ /* It is possible that we are currently running under setuid permissions */
static int maybe_setuid = 1; static int maybe_setuid = 1;
/* Name of the communication socket used for native gpg-agent requests. */ /* Name of the communication socket used for native gpg-agent
requests. The second variable is either NULL or a malloced string
with the real socket name in case it has been redirected. */
static char *socket_name; static char *socket_name;
static char *redir_socket_name;
/* Name of the optional extra socket used for native gpg-agent requests. */ /* Name of the optional extra socket used for native gpg-agent requests. */
static char *socket_name_extra; static char *socket_name_extra;
static char *redir_socket_name_extra;
/* Name of the communication socket used for ssh-agent-emulation. */ /* Name of the communication socket used for ssh-agent-emulation. */
static char *socket_name_ssh; static char *socket_name_ssh;
static char *redir_socket_name_ssh;
/* We need to keep track of the server's nonces (these are dummies for /* We need to keep track of the server's nonces (these are dummies for
POSIX systems). */ POSIX systems). */
@ -328,6 +337,7 @@ static int active_connections;
static char *create_socket_name (char *standard_name, int with_homedir); static char *create_socket_name (char *standard_name, int with_homedir);
static gnupg_fd_t create_server_socket (char *name, int primary, static gnupg_fd_t create_server_socket (char *name, int primary,
char **r_redir_name,
assuan_sock_nonce_t *nonce); assuan_sock_nonce_t *nonce);
static void create_directories (void); static void create_directories (void);
@ -472,14 +482,18 @@ set_debug (void)
} }
/* Helper for cleanup to remove one socket with NAME. */ /* Helper for cleanup to remove one socket with NAME. REDIR_NAME is
the corresponding real name if the socket has been redirected. */
static void static void
remove_socket (char *name) remove_socket (char *name, char *redir_name)
{ {
if (name && *name) if (name && *name)
{ {
char *p; char *p;
if (redir_name)
name = redir_name;
gnupg_remove (name); gnupg_remove (name);
p = strrchr (name, '/'); p = strrchr (name, '/');
if (p) if (p)
@ -504,10 +518,10 @@ cleanup (void)
return; return;
done = 1; done = 1;
deinitialize_module_cache (); deinitialize_module_cache ();
remove_socket (socket_name); remove_socket (socket_name, redir_socket_name);
if (opt.extra_socket > 1) if (opt.extra_socket > 1)
remove_socket (socket_name_extra); remove_socket (socket_name_extra, redir_socket_name_extra);
remove_socket (socket_name_ssh); remove_socket (socket_name_ssh, redir_socket_name_ssh);
} }
@ -1098,20 +1112,24 @@ main (int argc, char **argv )
/* Create the sockets. */ /* Create the sockets. */
socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1); socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
fd = create_server_socket (socket_name, 1, &socket_nonce); fd = create_server_socket (socket_name, 1,
&redir_socket_name, &socket_nonce);
if (opt.extra_socket) if (opt.extra_socket)
{ {
socket_name_extra = create_socket_name (socket_name_extra, 0); socket_name_extra = create_socket_name (socket_name_extra, 0);
opt.extra_socket = 2; /* Indicate that it has been malloced. */ opt.extra_socket = 2; /* Indicate that it has been malloced. */
fd_extra = create_server_socket (socket_name_extra, 0, fd_extra = create_server_socket (socket_name_extra, 0,
&redir_socket_name_extra,
&socket_nonce_extra); &socket_nonce_extra);
} }
if (opt.ssh_support) if (opt.ssh_support)
{ {
socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1); socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
fd_ssh = create_server_socket (socket_name_ssh, 0, &socket_nonce_ssh); fd_ssh = create_server_socket (socket_name_ssh, 0,
&redir_socket_name_ssh,
&socket_nonce_ssh);
} }
/* If we are going to exec a program in the parent, we record /* If we are going to exec a program in the parent, we record
@ -1499,11 +1517,6 @@ create_socket_name (char *standard_name, int with_homedir)
log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S); log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
agent_exit (2); agent_exit (2);
} }
if (strlen (name) + 1 >= DIMof (struct sockaddr_un, sun_path) )
{
log_error (_("name of socket too long\n"));
agent_exit (2);
}
return name; return name;
} }
@ -1512,33 +1525,69 @@ create_socket_name (char *standard_name, int with_homedir)
/* Create a Unix domain socket with NAME. Returns the file descriptor /* Create a Unix domain socket with NAME. Returns the file descriptor
or terminates the process in case of an error. Note that this or terminates the process in case of an error. Note that this
function needs to be used for the regular socket first (indicated function needs to be used for the regular socket first (indicated
by PRIMARY) and only then for the extra and the ssh sockets. */ by PRIMARY) and only then for the extra and the ssh sockets. if
the soecket has been redirected the name of the real socket is
stored as a malloced string at R_REDIR_NAME. */
static gnupg_fd_t static gnupg_fd_t
create_server_socket (char *name, int primary, assuan_sock_nonce_t *nonce) create_server_socket (char *name, int primary,
char **r_redir_name, assuan_sock_nonce_t *nonce)
{ {
struct sockaddr_un *serv_addr; struct sockaddr *addr;
struct sockaddr_un *unaddr;
socklen_t len; socklen_t len;
gnupg_fd_t fd; gnupg_fd_t fd;
int rc; int rc;
xfree (*r_redir_name);
*r_redir_name = NULL;
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0); fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD) if (fd == ASSUAN_INVALID_FD)
{ {
log_error (_("can't create socket: %s\n"), strerror (errno)); log_error (_("can't create socket: %s\n"), strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2); agent_exit (2);
} }
serv_addr = xmalloc (sizeof (*serv_addr)); unaddr = xmalloc (sizeof *unaddr);
memset (serv_addr, 0, sizeof *serv_addr); addr = (struct sockaddr*)unaddr;
serv_addr->sun_family = AF_UNIX;
if (strlen (name) + 1 >= sizeof (serv_addr->sun_path)) #if ASSUAN_VERSION_NUMBER >= 0x020104 /* >= 2.1.4 */
{
int redirected;
if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
{
if (errno == ENAMETOOLONG)
log_error (_("socket name '%s' is too long\n"), name);
else
log_error ("error preparing socket '%s': %s\n",
name, gpg_strerror (gpg_error_from_syserror ()));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
if (redirected)
{
*r_redir_name = xstrdup (unaddr->sun_path);
if (opt.verbose)
log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
}
}
#else /* Assuan < 2.1.4 */
redirected = 0;
memset (unaddr, 0, sizeof *unaddr);
unaddr->sun_family = AF_UNIX;
if (strlen (name) + 1 >= sizeof (unaddr->sun_path))
{ {
log_error (_("socket name '%s' is too long\n"), name); log_error (_("socket name '%s' is too long\n"), name);
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2); agent_exit (2);
} }
strcpy (serv_addr->sun_path, name); strcpy (unaddr->sun_path, name);
len = SUN_LEN (serv_addr); #endif /* Assuan < 2.1.4 */
rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
len = SUN_LEN (unaddr);
rc = assuan_sock_bind (fd, addr, len);
/* Our error code mapping on W32CE returns EEXIST thus we also test /* Our error code mapping on W32CE returns EEXIST thus we also test
for this. */ for this. */
@ -1549,14 +1598,13 @@ create_server_socket (char *name, int primary, assuan_sock_nonce_t *nonce)
#endif #endif
)) ))
{ {
/* Check whether a gpg-agent is already running. /* Check whether a gpg-agent is already running. We do this
We do this test only if this is not the ssh socket. test only if this is the primary socket. For secondary
For ssh we assume that a test for gpg-agent has already been sockets we assume that a test for gpg-agent has already been
done and reuse the requested ssh socket. Testing the done and reuse the requested socket. Testing the ssh-socket
ssh-socket is not possible because at this point, though we is not possible because at this point, though we know the new
know the new Assuan socket, the Assuan server and thus the Assuan socket, the Assuan server and thus the ssh-agent
ssh-agent server is not yet operational. This would lead to server is not yet operational; this would lead to a hang. */
a hang. */
if (primary && !check_for_running_agent (1)) if (primary && !check_for_running_agent (1))
{ {
log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX);
@ -1567,19 +1615,18 @@ create_server_socket (char *name, int primary, assuan_sock_nonce_t *nonce)
assuan_sock_close (fd); assuan_sock_close (fd);
agent_exit (2); agent_exit (2);
} }
gnupg_remove (name); gnupg_remove (unaddr->sun_path);
rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len); rc = assuan_sock_bind (fd, addr, len);
} }
if (rc != -1 if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce)))
&& (rc=assuan_sock_get_nonce ((struct sockaddr*)serv_addr, len, nonce)))
log_error (_("error getting nonce for the socket\n")); log_error (_("error getting nonce for the socket\n"));
if (rc == -1) if (rc == -1)
{ {
/* We use gpg_strerror here because it allows us to get strings /* We use gpg_strerror here because it allows us to get strings
for some W32 socket error codes. */ for some W32 socket error codes. */
log_error (_("error binding socket to '%s': %s\n"), log_error (_("error binding socket to '%s': %s\n"),
serv_addr->sun_path, unaddr->sun_path,
gpg_strerror (gpg_error_from_errno (errno))); gpg_strerror (gpg_error_from_syserror ()));
assuan_sock_close (fd); assuan_sock_close (fd);
*name = 0; /* Inhibit removal of the socket by cleanup(). */ *name = 0; /* Inhibit removal of the socket by cleanup(). */
@ -1589,12 +1636,13 @@ create_server_socket (char *name, int primary, assuan_sock_nonce_t *nonce)
if (listen (FD2INT(fd), 5 ) == -1) if (listen (FD2INT(fd), 5 ) == -1)
{ {
log_error (_("listen() failed: %s\n"), strerror (errno)); log_error (_("listen() failed: %s\n"), strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
assuan_sock_close (fd); assuan_sock_close (fd);
agent_exit (2); agent_exit (2);
} }
if (opt.verbose) if (opt.verbose)
log_info (_("listening on socket '%s'\n"), serv_addr->sun_path); log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
return fd; return fd;
} }