aboutsummaryrefslogtreecommitdiffstats
path: root/agent/gpg-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/gpg-agent.c')
-rw-r--r--agent/gpg-agent.c366
1 files changed, 261 insertions, 105 deletions
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 69a28e78b..5f7b6f843 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -30,6 +30,7 @@
#include <time.h>
#include <fcntl.h>
#include <sys/socket.h>
+#include <sys/select.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
@@ -84,8 +85,8 @@ enum cmd_and_opt_values
oAllowMarkTrusted,
oKeepTTY,
oKeepDISPLAY,
-
-aTest };
+ aTest,
+ oSSHSupport };
@@ -131,6 +132,8 @@ static ARGPARSE_OPTS opts[] = {
N_("do not use the PIN cache when signing")},
{ oAllowMarkTrusted, "allow-mark-trusted", 0,
N_("allow clients to mark keys as \"trusted\"")},
+ { oSSHSupport, "ssh-support", 0, "Enable SSH-Agent emulation" },
+
{0}
};
@@ -149,6 +152,9 @@ static int maybe_setuid = 1;
/* Name of the communication socket */
static char socket_name[128];
+/* Name of the communication socket used for ssh-agent-emulation. */
+static char socket_name_ssh[128];
+
/* Default values for options passed to the pinentry. */
static char *default_display;
static char *default_ttyname;
@@ -169,7 +175,7 @@ static char *current_logfile;
/* Local prototypes. */
static void create_directories (void);
#ifdef USE_GNU_PTH
-static void handle_connections (int listen_fd);
+static void handle_connections (int listen_fd, int listen_fd_ssh);
/* Pth wrapper function definitions. */
GCRY_THREAD_OPTION_PTH_IMPL;
@@ -280,25 +286,30 @@ set_debug (void)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
}
-
static void
-cleanup (void)
+cleanup_do (char *name)
{
- if (*socket_name)
- {
- char *p;
+ char *p = NULL;
- remove (socket_name);
- p = strrchr (socket_name, '/');
- if (p)
- {
- *p = 0;
- rmdir (socket_name);
- *p = '/';
- }
- *socket_name = 0;
+ remove (name);
+ p = strrchr (name, '/');
+ if (p)
+ {
+ *p = 0;
+ rmdir (name);
+ *p = '/';
}
+ *name = 0;
+}
+
+static void
+cleanup (void)
+{
+ if (*socket_name)
+ cleanup_do (socket_name);
+ if (*socket_name_ssh)
+ cleanup_do (socket_name_ssh);
}
@@ -383,6 +394,76 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
return 1; /* handled */
}
+static void
+server_socket_create (int *sock,
+ const char *identifier, char *name, int name_size)
+{
+ struct sockaddr_un serv_addr;
+ char *p = NULL;
+ int fd = -1;
+ socklen_t len;
+
+ *name = 0;
+ snprintf (name, name_size - 1,
+ "/tmp/gpg-XXXXXX/S.gpg-agent%s",
+ identifier ? identifier : "");
+ name[name_size - 1] = 0;
+
+ if (strchr (name, ':'))
+ {
+ log_error ("colons are not allowed in the socket name\n");
+ exit (1);
+ }
+
+ p = strrchr (name, '/');
+ if (! p)
+ BUG ();
+ *p = 0;;
+ if (! mkdtemp (name))
+ {
+ log_error ("can't create directory `%s': %s\n",
+ name, strerror (errno));
+ exit (1);
+ }
+ *p = '/';
+
+ if (strlen (name) + 1 >= sizeof (serv_addr.sun_path))
+ {
+ log_error ("name of socket too long\n");
+ exit (1);
+ }
+
+ fd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ log_error ("can't create socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ memset (&serv_addr, 0, sizeof serv_addr);
+ serv_addr.sun_family = AF_UNIX;
+ strcpy (serv_addr.sun_path, name);
+ len = (offsetof (struct sockaddr_un, sun_path)
+ + strlen (serv_addr.sun_path) + 1);
+
+ if (bind (fd, (struct sockaddr*) &serv_addr, len) == -1)
+ {
+ log_error ("error binding socket to `%s': %s\n",
+ serv_addr.sun_path, strerror (errno));
+ close (fd);
+ exit (1);
+ }
+
+ if (listen (fd, 5) == -1)
+ {
+ log_error ("listen() failed: %s\n", strerror (errno));
+ close (fd);
+ exit (1);
+ }
+
+ *sock = fd;
+}
+
int
main (int argc, char **argv )
@@ -568,6 +649,12 @@ main (int argc, char **argv )
case oKeepTTY: opt.keep_tty = 1; break;
case oKeepDISPLAY: opt.keep_display = 1; break;
+ case oSSHSupport:
+ opt.ssh_support = 1;
+ opt.keep_tty = 1;
+ opt.keep_display = 1;
+ break;
+
default : pargs.err = configfp? 1:2; break;
}
}
@@ -702,12 +789,10 @@ main (int argc, char **argv )
else if (!is_daemon)
;
else
- { /* regular server mode */
- int fd;
+ { /* regular server mode */
+ int fd = -1, fd_ssh = -1;
pid_t pid;
- int len;
- struct sockaddr_un serv_addr;
- char *p;
+ //char *p;
/* Remove the DISPLAY variable so that a pinentry does not
default to a specific display. There is still a default
@@ -716,65 +801,18 @@ main (int argc, char **argv )
if (!opt.keep_display)
unsetenv ("DISPLAY");
- *socket_name = 0;
- snprintf (socket_name, DIM(socket_name)-1,
- "/tmp/gpg-XXXXXX/S.gpg-agent");
- socket_name[DIM(socket_name)-1] = 0;
- p = strrchr (socket_name, '/');
- if (!p)
- BUG ();
- *p = 0;;
- if (!mkdtemp(socket_name))
- {
- log_error ("can't create directory `%s': %s\n",
- socket_name, strerror(errno) );
- exit (1);
- }
- *p = '/';
+ server_socket_create (&fd, "", socket_name, sizeof (socket_name));
+ if (opt.ssh_support)
+ server_socket_create (&fd_ssh, "-ssh",
+ socket_name_ssh, sizeof (socket_name_ssh));
- if (strchr (socket_name, ':') )
- {
- log_error ("colons are not allowed in the socket name\n");
- exit (1);
- }
- if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
- {
- log_error ("name of socket too long\n");
- exit (1);
- }
-
-
- fd = socket (AF_UNIX, SOCK_STREAM, 0);
- if (fd == -1)
- {
- log_error ("can't create socket: %s\n", strerror(errno) );
- exit (1);
- }
-
- memset (&serv_addr, 0, sizeof serv_addr);
- serv_addr.sun_family = AF_UNIX;
- strcpy (serv_addr.sun_path, socket_name);
- len = (offsetof (struct sockaddr_un, sun_path)
- + strlen(serv_addr.sun_path) + 1);
-
- if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
- {
- log_error ("error binding socket to `%s': %s\n",
- serv_addr.sun_path, strerror (errno) );
- close (fd);
- exit (1);
- }
-
- if (listen (fd, 5 ) == -1)
- {
- log_error ("listen() failed: %s\n", strerror (errno));
- close (fd);
- exit (1);
- }
if (opt.verbose)
- log_info ("listening on socket `%s'\n", socket_name );
-
+ {
+ log_info ("listening on socket `%s'\n", socket_name);
+ if (opt.ssh_support)
+ log_info ("listening on socket `%s'\n", socket_name_ssh);
+ }
fflush (NULL);
pid = fork ();
@@ -785,9 +823,11 @@ main (int argc, char **argv )
}
else if (pid)
{ /* We are the parent */
- char *infostr;
+ char *infostr, *infostr_ssh_sock, *infostr_ssh_pid;
close (fd);
+ if (opt.ssh_support)
+ close (fd_ssh);
/* create the info string: <name>:<pid>:<protocol_version> */
if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
@@ -797,8 +837,28 @@ main (int argc, char **argv )
kill (pid, SIGTERM);
exit (1);
}
+ if (opt.ssh_support)
+ {
+ if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
+ socket_name_ssh) < 0)
+ {
+ log_error ("out of core\n");
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ if (asprintf (&infostr_ssh_pid, "SSH_AGENT_PID=%u",
+ pid) < 0)
+ {
+ log_error ("out of core\n");
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ }
+
*socket_name = 0; /* don't let cleanup() remove the socket -
the child should do this from now on */
+ *socket_name_ssh = 0;
+
if (argc)
{ /* run the program given on the commandline */
if (putenv (infostr))
@@ -808,6 +868,20 @@ main (int argc, char **argv )
kill (pid, SIGTERM );
exit (1);
}
+ if (putenv (infostr_ssh_sock))
+ {
+ log_error ("failed to set environment: %s\n",
+ strerror (errno) );
+ kill (pid, SIGTERM );
+ exit (1);
+ }
+ if (putenv (infostr_ssh_pid))
+ {
+ log_error ("failed to set environment: %s\n",
+ strerror (errno) );
+ kill (pid, SIGTERM );
+ exit (1);
+ }
execvp (argv[0], argv);
log_error ("failed to run the command: %s\n", strerror (errno));
kill (pid, SIGTERM);
@@ -821,12 +895,29 @@ main (int argc, char **argv )
{
*strchr (infostr, '=') = ' ';
printf ( "setenv %s\n", infostr);
+ if (opt.ssh_support)
+ {
+ *strchr (infostr_ssh_sock, '=') = ' ';
+ printf ( "setenv %s\n", infostr_ssh_sock);
+ *strchr (infostr_ssh_pid, '=') = ' ';
+ printf ( "setenv %s\n", infostr_ssh_pid);
+ }
}
else
{
printf ( "%s; export GPG_AGENT_INFO;\n", infostr);
+ if (opt.ssh_support)
+ {
+ printf ( "%s; export SSH_AUTH_SOCK;\n", infostr_ssh_sock);
+ printf ( "%s; export SSH_AGENT_PID;\n", infostr_ssh_pid);
+ }
}
free (infostr);
+ if (opt.ssh_support)
+ {
+ free (infostr_ssh_sock);
+ free (infostr_ssh_pid);
+ }
exit (0);
}
/*NEVER REACHED*/
@@ -877,7 +968,7 @@ main (int argc, char **argv )
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
sigaction (SIGPIPE, &sa, NULL);
- handle_connections (fd);
+ handle_connections (fd, opt.ssh_support ? fd_ssh : -1);
}
else
#endif /*!USE_GNU_PTH*/
@@ -903,7 +994,7 @@ main (int argc, char **argv )
}
close (fd);
}
-
+
return 0;
}
@@ -1144,24 +1235,40 @@ start_connection_thread (void *arg)
return NULL;
}
+static void *
+start_connection_thread_ssh (void *arg)
+{
+ int fd = (int)arg;
+
+ if (opt.verbose)
+ log_info ("ssh handler for fd %d started\n", fd);
+
+ /* FIXME: Move this housekeeping into a ticker function. Calling it
+ for each connection should work but won't work anymore if our
+ cleints start to keep connections. */
+ agent_trustlist_housekeeping ();
+
+ start_command_handler_ssh (fd);
+ if (opt.verbose)
+ log_info ("ssh handler for fd %d terminated\n", fd);
+
+ return NULL;
+}
static void
-handle_connections (int listen_fd)
+handle_connections (int listen_fd, int listen_fd_ssh)
{
- pth_attr_t tattr;
pth_event_t ev;
sigset_t sigs;
int signo;
struct sockaddr_un paddr;
socklen_t plen = sizeof( paddr );
+ pth_attr_t tattr;
+ fd_set fdset, read_fdset;
+ int ret = -1;
int fd;
- tattr = pth_attr_new();
- pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
- pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
- pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
-
- sigemptyset (&sigs );
+ sigemptyset (&sigs);
sigaddset (&sigs, SIGHUP);
sigaddset (&sigs, SIGUSR1);
sigaddset (&sigs, SIGUSR2);
@@ -1169,6 +1276,16 @@ handle_connections (int listen_fd)
sigaddset (&sigs, SIGTERM);
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+ tattr = pth_attr_new();
+ pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
+ pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
+ pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
+
+ FD_ZERO (&fdset);
+ FD_SET (listen_fd, &fdset);
+ if (listen_fd_ssh != -1)
+ FD_SET (listen_fd_ssh, &fdset);
+
for (;;)
{
if (shutdown_pending)
@@ -1185,28 +1302,67 @@ handle_connections (int listen_fd)
continue;
}
- fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
- if (fd == -1)
- {
+ read_fdset = fdset;
+ ret = pth_select (FD_SETSIZE, &read_fdset, NULL, NULL, NULL);
+ if (ret == -1)
+ {
+ log_error ("pth_select failed: %s - waiting 1s\n",
+ strerror (errno));
+ pth_sleep (1);
+ continue;
+ }
+
+ if (FD_ISSET (listen_fd, &read_fdset))
+ {
+ fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
+ if (fd == -1)
+ {
#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */
- if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
+ if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
#else
- if (pth_event_occurred (ev))
+ if (pth_event_occurred (ev))
#endif
- {
- handle_signal (signo);
- continue;
+ {
+ handle_signal (signo);
+ continue;
+ }
+ log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
+ pth_sleep(1);
+ continue;
+ }
+
+ if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
+ {
+ log_error ("error spawning connection handler: %s\n",
+ strerror (errno) );
+ close (fd);
}
- log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
- pth_sleep(1);
- continue;
}
+ else if ((listen_fd_ssh != -1) && FD_ISSET (listen_fd_ssh, &read_fdset))
+ {
+ fd = pth_accept_ev (listen_fd_ssh, (struct sockaddr *)&paddr, &plen, ev);
+ if (fd == -1)
+ {
+#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */
+ if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
+#else
+ if (pth_event_occurred (ev))
+#endif
+ {
+ handle_signal (signo);
+ continue;
+ }
+ log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
+ pth_sleep(1);
+ continue;
+ }
- if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
- {
- log_error ("error spawning connection handler: %s\n",
- strerror (errno) );
- close (fd);
+ if (!pth_spawn (tattr, start_connection_thread_ssh, (void*)fd))
+ {
+ log_error ("error spawning connection handler: %s\n",
+ strerror (errno) );
+ close (fd);
+ }
}
}