1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-10 23:49:50 +02:00

2004-07-19 Moritz Schulte <moritz@g10code.com>

* Makefile.am (gpg_agent_SOURCES): Adding: gpg-stream.c,
	gpg-stream.h, buffer.c, buffer.h, command-ssh.c.

	* pksign.c (agent_pksign_do): New function, based on code ripped
	out from agent_pksign.
	(agent_pksign): Use agent_pksign_do.

	* query.c (start_pinentry): Accept CTRL being NULL.

	* agent.h (start_command_handler_ssh): Declare function.
	(agent_pksign_do): Declare function.
	(opt): New member: ssh_support.

	* gpg-agent.c: Include <sys/select.h>.
	New configuration option: ssh-support.
	(socket_name_ssh): New variabel.
	(handle_connections): Additional argument: listen_fd_ssh.  Accept
	connections on both sockets, call start_connection_thread_ssh for
	connections on listen_fd_ssh.
	(start_connection_thread_ssh): New function.
	(cleanup_do): New functions, basically old cleanup function.
	(cleanup): Call cleanup_do for socket_name and socket_name_ssh.
	(server_socket_create): New function ...
	(main): ... use it.
	(main): Generate environment entries for ssh.

	* command-ssh.c: New file, implementing the ssh-agent protocol.
	* gpg-stream.c, gpg-stream.h, buffer.c, buffer.h: Merged
	Libgpg-stream.
This commit is contained in:
Moritz Schulte 2004-07-19 15:54:11 +00:00
parent 6decea8316
commit 15664a8598
11 changed files with 3643 additions and 141 deletions

View File

@ -1,3 +1,35 @@
2004-07-19 Moritz Schulte <moritz@g10code.com>
* Makefile.am (gpg_agent_SOURCES): Adding: gpg-stream.c,
gpg-stream.h, buffer.c, buffer.h, command-ssh.c.
* pksign.c (agent_pksign_do): New function, based on code ripped
out from agent_pksign.
(agent_pksign): Use agent_pksign_do.
* query.c (start_pinentry): Accept CTRL being NULL.
* agent.h (start_command_handler_ssh): Declare function.
(agent_pksign_do): Declare function.
(opt): New member: ssh_support.
* gpg-agent.c: Include <sys/select.h>.
New configuration option: ssh-support.
(socket_name_ssh): New variabel.
(handle_connections): Additional argument: listen_fd_ssh. Accept
connections on both sockets, call start_connection_thread_ssh for
connections on listen_fd_ssh.
(start_connection_thread_ssh): New function.
(cleanup_do): New functions, basically old cleanup function.
(cleanup): Call cleanup_do for socket_name and socket_name_ssh.
(server_socket_create): New function ...
(main): ... use it.
(main): Generate environment entries for ssh.
* command-ssh.c: New file, implementing the ssh-agent protocol.
* gpg-stream.c, gpg-stream.h, buffer.c, buffer.h: Merged
Libgpg-stream.
2004-06-20 Moritz Schulte <moritz@g10code.com>
* gpg-agent.c: Include <sys/stat.h> (build fix for BSD).

View File

@ -29,7 +29,9 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
gpg_agent_SOURCES = \
gpg-agent.c agent.h \
command.c \
gpg-stream.c gpg-stream.h gpg-stream-config.h \
buffer.c buffer.h \
command.c command-ssh.c \
query.c \
cache.c \
trans.c \

View File

@ -61,6 +61,7 @@ struct {
int allow_mark_trusted;
int keep_tty; /* don't switch the TTY (for pinentry) on request */
int keep_display; /* don't switch the DISPLAY (for pinentry) on request */
int ssh_support; /* enable SSH-Agent emulation. */
} opt;
@ -130,6 +131,9 @@ void agent_init_default_ctrl (struct server_control_s *ctrl);
/*-- command.c --*/
void start_command_handler (int, int);
/*-- command-ssh.c --*/
void start_command_handler_ssh (int);
/*-- findkey.c --*/
int agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force);
@ -158,6 +162,8 @@ void agent_unlock_cache_entry (void **cache_id);
/*-- pksign.c --*/
int agent_pksign_do (CTRL ctrl, const char *desc_text,
gcry_sexp_t *signature_sexp, int ignore_cache);
int agent_pksign (ctrl_t ctrl, const char *desc_text,
FILE *outfp, int ignore_cache);

413
agent/buffer.c Normal file
View File

@ -0,0 +1,413 @@
/* buffer.c - Buffer management layer
Copyright (C) 2004 g10 Code GmbH
This file is part of libgpg-stream.
libgpg-stream is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
libgpg-stream is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with libgpg-stream; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <gpg-error.h>
#include "buffer.h"
/* Buffer context. */
struct buffer
{
void *handle; /* Handle, passed to callbacks. */
buffer_functions_t functions; /* Callback functions. */
unsigned int flags; /* General flags. */
struct buffer_in
{
char *container; /* Container holding data. */
size_t container_size; /* Size of CONTAINER. */
size_t data_size; /* Size of data in CONTAINER. */
off_t data_offset; /* Offset inside of CONTAINER. */
} buffer_in;
struct buffer_out
{
char *container; /* Container holding data. */
size_t container_size; /* Size of CONTAINER. */
size_t data_size; /* Size of data in CONTAINER. */
off_t data_offset; /* Offset inside of CONTAINER. */
size_t data_flushed; /* Amount of data already flushed. */
} buffer_out;
};
/* Buffer contains unflushed data. */
#define BUFFER_FLAG_DIRTY (1 << 0)
/* Fill buffer. */
static gpg_error_t
buffer_fill_do (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t bytes_read = 0;
if (! buffer->functions.func_read)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
{
buffer_func_read_t func_read = buffer->functions.func_read;
err = (*func_read) (buffer->handle,
buffer->buffer_in.container,
buffer->buffer_in.container_size,
&bytes_read);
}
buffer->buffer_in.data_offset = 0;
buffer->buffer_in.data_size = bytes_read;
return err;
}
/* Empty buffer input. */
static gpg_error_t
buffer_empty (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
buffer->buffer_in.data_size = 0;
buffer->buffer_in.data_offset = 0;
return err;
}
/* Flush data contained in buffer. */
static gpg_error_t
buffer_flush_do (buffer_t buffer)
{
buffer_func_write_t func_write = buffer->functions.func_write;
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t bytes_written = 0;
if (! func_write)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else if (buffer->flags & BUFFER_FLAG_DIRTY)
while ((buffer->buffer_out.data_size
- buffer->buffer_out.data_flushed) && (! err))
{
err = (*func_write) (buffer->handle,
buffer->buffer_out.container
+ buffer->buffer_out.data_flushed,
buffer->buffer_out.data_size
- buffer->buffer_out.data_flushed,
&bytes_written);
if (! err)
{
buffer->buffer_out.data_size = 0;
buffer->buffer_out.data_offset = 0;
buffer->buffer_out.data_flushed = 0;
buffer->flags &= ~BUFFER_FLAG_DIRTY;
}
else
buffer->buffer_out.data_flushed += bytes_written;
}
return err;
}
static gpg_error_t
buffer_stat_do (buffer_t buffer,
size_t *size)
{
buffer_func_stat_t func_stat = buffer->functions.func_stat;
gpg_error_t err = GPG_ERR_NO_ERROR;
err = (*func_stat) (buffer->handle, size);
return err;
}
/* Read from a buffer. */
gpg_error_t
buffer_read (buffer_t buffer,
char *data,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t data_read = 0;
size_t data_to_copy = 0;
while ((bytes_to_read - data_read) && (! err))
{
if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size)
{
/* Nothing more to read in current container, try to
fill container with new data. */
err = buffer_fill_do (buffer);
if (! err)
if (! buffer->buffer_in.data_size)
/* Filling did not result in any data read. */
break;
}
if (! err)
{
if ((bytes_to_read
- data_read) > (buffer->buffer_in.data_size
- buffer->buffer_in.data_offset))
data_to_copy = (buffer->buffer_in.data_size
- buffer->buffer_in.data_offset);
else
data_to_copy = bytes_to_read - data_read;
memcpy (data + data_read,
buffer->buffer_in.container + buffer->buffer_in.data_offset,
data_to_copy);
buffer->buffer_in.data_offset += data_to_copy;
data_read += data_to_copy;
}
}
if (bytes_read)
*bytes_read = data_read;
return err;
}
/* Write to a buffer. */
gpg_error_t
buffer_write (buffer_t buffer,
const char *data,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t data_written = 0;
size_t data_to_copy = 0;
while ((bytes_to_write - data_written) && (! err))
{
if (buffer->buffer_out.data_offset == buffer->buffer_out.container_size)
/* Container full, flush buffer. */
err = buffer_flush_do (buffer);
if (! err)
{
if ((bytes_to_write
- data_written) > (buffer->buffer_out.container_size
- buffer->buffer_out.data_offset))
data_to_copy = (buffer->buffer_out.container_size
- buffer->buffer_out.data_offset);
else
data_to_copy = bytes_to_write - data_written;
memcpy (buffer->buffer_out.container
+ buffer->buffer_out.data_offset,
data + data_written,
data_to_copy);
if ((buffer->buffer_out.data_offset
+ data_to_copy) > buffer->buffer_out.data_size)
buffer->buffer_out.data_size = (buffer->buffer_out.data_offset
+ data_to_copy);
buffer->buffer_out.data_offset += data_to_copy;
data_written += data_to_copy;
if (data_written)
if (! (buffer->flags & BUFFER_FLAG_DIRTY))
buffer->flags |= BUFFER_FLAG_DIRTY;
}
}
if (bytes_written)
*bytes_written = data_written;
return err;
}
/* Seek in a buffer. */
gpg_error_t
buffer_seek (buffer_t buffer,
off_t offset,
int whence)
{
buffer_func_seek_t func_seek = buffer->functions.func_seek;
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! func_seek)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
{
if (buffer->flags & BUFFER_FLAG_DIRTY)
/* Flush data first in order to prevent flushing it to the
wrong offset. */
err = buffer_flush_do (buffer);
if (! err)
err = (*func_seek) (buffer->handle, offset, whence);
if (! err)
err = buffer_empty (buffer);
}
return err;
}
/* Return the unread data contained in a buffer. */
gpg_error_t
buffer_peek (buffer_t buffer,
char **data,
size_t *data_size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size)
/* Refill container. */
err = buffer_fill_do (buffer);
if (! err)
{
if (data)
*data = buffer->buffer_in.container + buffer->buffer_in.data_offset;
if (data_size)
*data_size = buffer->buffer_in.data_size - buffer->buffer_in.data_offset;
}
return err;
}
/* Skip SIZE bytes of input data contained in buffer. */
gpg_error_t
buffer_skip (buffer_t buffer,
size_t size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer->buffer_in.data_offset + size > buffer->buffer_in.data_size)
err = gpg_error (GPG_ERR_INV_ARG);
else
buffer->buffer_in.data_offset += size;
return err;
}
/* Create a new buffer. */
gpg_error_t
buffer_create (buffer_t *buffer,
void *handle,
buffer_functions_t functions)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
/* Allocate memory, initialize. */
buffer_t buffer_new = NULL;
char *container_in_new = NULL;
char *container_out_new = NULL;
buffer_new = malloc (sizeof (*buffer_new));
if (! buffer_new)
err = gpg_error_from_errno (errno);
if (! err)
{
container_in_new = malloc (BUFFER_BLOCK_SIZE);
if (! container_in_new)
err = gpg_error_from_errno (errno);
}
if (! err)
{
container_out_new = malloc (BUFFER_BLOCK_SIZE);
if (! container_out_new)
err = gpg_error_from_errno (errno);
}
if (! err)
{
buffer_new->handle = handle;
buffer_new->flags = 0;
buffer_new->functions = functions;
buffer_new->buffer_in.container = container_in_new;
buffer_new->buffer_in.container_size = BUFFER_BLOCK_SIZE;
buffer_new->buffer_in.data_size = 0;
buffer_new->buffer_in.data_offset = 0;
buffer_new->buffer_out.container = container_out_new;
buffer_new->buffer_out.container_size = BUFFER_BLOCK_SIZE;
buffer_new->buffer_out.data_size = 0;
buffer_new->buffer_out.data_offset = 0;
buffer_new->buffer_out.data_flushed = 0;
*buffer = buffer_new;
}
else
{
if (container_in_new)
free (container_in_new);
if (container_out_new)
free (container_out_new);
if (buffer_new)
free (buffer_new);
}
return err;
}
/* Destroy a buffer. */
gpg_error_t
buffer_destroy (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer)
{
err = buffer_flush_do (buffer);
free (buffer->buffer_in.container);
free (buffer->buffer_out.container);
free (buffer);
}
return err;
}
/* Write out unwritten data contained in buffer. */
gpg_error_t
buffer_flush (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_flush_do (buffer);
return err;
}
/* Stat buffer. */
gpg_error_t
buffer_stat (buffer_t buffer,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_stat_do (buffer, size);
return err;
}

102
agent/buffer.h Normal file
View File

@ -0,0 +1,102 @@
/* buffer.h - Buffer management layer
Copyright (C) 2004 g10 Code GmbH
This file is part of libgpg-stream.
libgpg-stream is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
libgpg-stream is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with libgpg-stream; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#ifndef BUFFER_H
#define BUFFER_H
#include <sys/types.h>
#include <stdarg.h>
#include <gpg-error.h>
#define BUFFER_BLOCK_SIZE 1024
typedef struct buffer *buffer_t;
/* Callbacks, necessary for filling/flushing/seeking. */
typedef gpg_error_t (*buffer_func_read_t) (void *handle,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read);
typedef gpg_error_t (*buffer_func_write_t) (void *handle,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written);
typedef gpg_error_t (*buffer_func_seek_t) (void *handle,
off_t offset,
int whence);
typedef gpg_error_t (*buffer_func_stat_t) (void *handle,
size_t *size);
typedef struct buffer_functions
{
buffer_func_read_t func_read; /* Read callback. */
buffer_func_write_t func_write; /* Write callback. */
buffer_func_seek_t func_seek; /* Seek callback. */
buffer_func_stat_t func_stat; /* Stat callback. */
} buffer_functions_t;
/* Create a new buffer. */
gpg_error_t buffer_create (buffer_t *buffer,
void *handle,
buffer_functions_t functions);
/* Destroy a buffer. */
gpg_error_t buffer_destroy (buffer_t buffer);
/* Read from a buffer. */
gpg_error_t buffer_read (buffer_t buffer,
char *data,
size_t bytes_to_read,
size_t *bytes_read);
/* Write to a buffer. */
gpg_error_t buffer_write (buffer_t buffer,
const char *data,
size_t bytes_to_write,
size_t *bytes_written);
/* Seek in a buffer. */
gpg_error_t buffer_seek (buffer_t buffer,
off_t offset,
int whence);
/* Return the unread data contained in a buffer. */
gpg_error_t buffer_peek (buffer_t buffer,
char **data,
size_t *data_size);
/* Skip SIZE bytes of input data contained in buffer. */
gpg_error_t buffer_skip (buffer_t buffer,
size_t size);
/* Write out unwritten data contained in buffer. */
gpg_error_t buffer_flush (buffer_t buffer);
/* Stat buffer. */
gpg_error_t buffer_stat (buffer_t buffer,
size_t *size);
#endif

1694
agent/command-ssh.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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_do (char *name)
{
char *p = NULL;
remove (name);
p = strrchr (name, '/');
if (p)
{
*p = 0;
rmdir (name);
*p = '/';
}
*name = 0;
}
static void
cleanup (void)
{
if (*socket_name)
{
char *p;
remove (socket_name);
p = strrchr (socket_name, '/');
if (p)
{
*p = 0;
rmdir (socket_name);
*p = '/';
}
*socket_name = 0;
}
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)
{
#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;
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 (!pth_spawn (tattr, start_connection_thread, (void*)fd))
{
log_error ("error spawning connection handler: %s\n",
strerror (errno) );
close (fd);
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)
#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);
}
}
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_ssh, (void*)fd))
{
log_error ("error spawning connection handler: %s\n",
strerror (errno) );
close (fd);
}
}
}

914
agent/gpg-stream.c Normal file
View File

@ -0,0 +1,914 @@
/* stream.c - Stream I/O/ layer
Copyright (C) 2004 g10 Code GmbH
This file is part of libgpg-stream.
libgpg-stream is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
libgpg-stream is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with libgpg-stream; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <gpg-error.h>
#include "gpg-stream.h"
#include "gpg-stream-config.h"
#include "buffer.h"
/* A Stream Context. */
struct gpg_stream
{
void *handle; /* Handle. */
unsigned int flags; /* Flags. */
buffer_t buffer; /* Buffer used for I/O. */
gpg_stream_functions_t functions; /* Callbacks. */
};
/* Macros. */
/* Standard permissions used for creating new files. */
#define GPG_STREAM_FILE_PERMISSIONS 0600
/* Evaluate EXPRESSION, setting VARIABLE to the return code, if
VARIABLE is zero. */
#define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \
do \
{ \
tmp_variable = expression; \
if ((! variable) && tmp_variable) \
variable = tmp_variable; \
} \
while (0)
/* Implementation of Memory I/O. */
typedef struct gpg_stream_handle_mem
{
char *memory; /* Data. */
size_t memory_size; /* Size of MEMORY. */
size_t data_size; /* Size of data in MEMORY. */
unsigned int grow: 1; /* MEMORY is allowed to grow. */
size_t offset; /* Current offset in MEMORY. */
} *gpg_stream_handle_mem_t;
static gpg_error_t
gpg_stream_func_mem_create (void **handle,
void *spec,
unsigned int flags)
{
gpg_stream_handle_mem_t mem_handle = NULL;
gpg_stream_spec_mem_t *mem_spec = spec;
gpg_error_t err = GPG_ERR_NO_ERROR;
mem_handle = malloc (sizeof (*mem_handle));
if (! mem_handle)
err = gpg_error_from_errno (errno);
else
{
mem_handle->memory = mem_spec ? mem_spec->memory : 0;
mem_handle->memory_size = mem_spec ? mem_spec->memory_size : 0;
mem_handle->data_size = 0;
mem_handle->grow = mem_spec ? mem_spec->grow : 1;
mem_handle->offset = 0;
*handle = mem_handle;
}
return err;
}
static gpg_error_t
gpg_stream_func_mem_read (void *handle,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_stream_handle_mem_t mem_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
if (bytes_to_read > mem_handle->data_size - mem_handle->offset)
bytes_to_read = mem_handle->data_size - mem_handle->offset;
memcpy (buffer, mem_handle->memory + mem_handle->offset,
bytes_to_read);
mem_handle->offset += bytes_to_read;
*bytes_read = bytes_to_read;
return err;
}
static gpg_error_t
gpg_stream_func_mem_write (void *handle,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_stream_handle_mem_t mem_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
char *memory_new = NULL;
if (! mem_handle->grow)
if (bytes_to_write > mem_handle->memory_size - mem_handle->offset)
bytes_to_write = mem_handle->memory_size - mem_handle->offset;
while (bytes_to_write > mem_handle->memory_size - mem_handle->offset)
{
memory_new = realloc (mem_handle->memory,
mem_handle->memory_size + BUFFER_BLOCK_SIZE);
if (! memory_new)
err = gpg_error_from_errno (errno);
else
{
if (mem_handle->memory != memory_new)
mem_handle->memory = memory_new;
mem_handle->memory_size += BUFFER_BLOCK_SIZE;
}
}
if (! err)
{
memcpy (mem_handle->memory + mem_handle->offset, buffer,
bytes_to_write);
if (mem_handle->offset + bytes_to_write > mem_handle->data_size)
mem_handle->data_size = mem_handle->offset + bytes_to_write;
mem_handle->offset += bytes_to_write;
}
*bytes_written = bytes_to_write;
return err;
}
gpg_error_t
gpg_stream_func_mem_seek (void *handle,
off_t offset,
int whence)
{
gpg_stream_handle_mem_t mem_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
switch (whence)
{
case SEEK_SET:
if ((offset < 0) || (offset > mem_handle->data_size))
err = gpg_error (GPG_ERR_INV_ARG);
else
mem_handle->offset = offset;
break;
case SEEK_CUR:
if ((mem_handle->offset + offset < 0)
|| (mem_handle->offset + offset > mem_handle->data_size))
err = gpg_error (GPG_ERR_INV_ARG);
else
mem_handle->offset += offset;
break;
case SEEK_END:
if ((mem_handle->data_size + offset < 0)
|| (mem_handle->data_size + offset > mem_handle->data_size))
err = gpg_error (GPG_ERR_INV_ARG);
else
mem_handle->offset += offset;
}
return err;
}
static gpg_error_t
gpg_stream_func_mem_stat (void *handle,
size_t *size)
{
gpg_stream_handle_mem_t mem_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
*size = mem_handle->data_size;
return err;
}
static gpg_error_t
gpg_stream_func_mem_destroy (void *handle)
{
gpg_stream_handle_mem_t mem_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
if (mem_handle->memory)
free (mem_handle->memory);
free (mem_handle);
return err;
}
gpg_stream_functions_t gpg_stream_functions_mem =
{
gpg_stream_func_mem_create,
gpg_stream_func_mem_read,
gpg_stream_func_mem_write,
gpg_stream_func_mem_seek,
gpg_stream_func_mem_stat,
gpg_stream_func_mem_destroy
};
/* Implementation of FD I/O. */
typedef struct gpg_stream_handle_fd
{
int fd;
} *gpg_stream_handle_fd_t;
static gpg_error_t
gpg_stream_func_fd_create (void **handle,
void *spec,
unsigned int flags)
{
gpg_stream_handle_fd_t fd_handle = NULL;
gpg_stream_spec_fd_t *fd_spec = spec;
gpg_error_t err = GPG_ERR_NO_ERROR;
fd_handle = malloc (sizeof (*fd_handle));
if (! fd_handle)
err = gpg_error_from_errno (errno);
else
{
fd_handle->fd = fd_spec->fd;
*handle = fd_handle;
}
return err;
}
static gpg_error_t
gpg_stream_func_fd_read (void *handle,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
ssize_t ret = -1;
ret = READ (file_handle->fd, buffer, bytes_to_read);
if (ret == -1)
err = gpg_error_from_errno (errno);
else
*bytes_read = ret;
return err;
}
static gpg_error_t
gpg_stream_func_fd_write (void *handle,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
ssize_t ret = -1;
ret = WRITE (file_handle->fd, buffer, bytes_to_write);
if (ret == -1)
err = gpg_error_from_errno (errno);
else
*bytes_written = ret;
return err;
}
static gpg_error_t
gpg_stream_func_fd_seek (void *handle,
off_t pos,
int whence)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
off_t ret = -1;
ret = lseek (file_handle->fd, pos, whence);
if (ret == -1)
err = gpg_error_from_errno (errno);
return err;
}
static gpg_error_t
gpg_stream_func_fd_stat (void *handle,
size_t *size)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
struct stat statbuf;
int ret = 0;
ret = fstat (file_handle->fd, &statbuf);
if (ret == -1)
err = gpg_error_from_errno (errno);
else
*size = statbuf.st_size;
return err;
}
static gpg_error_t
gpg_stream_func_fd_destroy (void *handle)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
free (file_handle);
return err;
}
gpg_stream_functions_t gpg_stream_functions_fd =
{
gpg_stream_func_fd_create,
gpg_stream_func_fd_read,
gpg_stream_func_fd_write,
gpg_stream_func_fd_seek,
gpg_stream_func_fd_stat,
gpg_stream_func_fd_destroy
};
/* Implementation of File I/O. */
static gpg_error_t
gpg_stream_func_file_create (void **handle,
void *spec,
unsigned int flags)
{
gpg_stream_handle_fd_t file_handle = NULL;
gpg_stream_spec_file_t *file_spec = spec;
gpg_error_t err = GPG_ERR_NO_ERROR;
int open_flags = 0;
int fd = -1;
file_handle = malloc (sizeof (*file_handle));
if (! file_handle)
err = gpg_error_from_errno (errno);
if (! err)
{
struct flag_mapping
{
unsigned int gpg_stream;
unsigned int sys;
} flag_mappings[] = { { GPG_STREAM_FLAG_READ,
O_RDONLY },
{ GPG_STREAM_FLAG_WRITE,
O_WRONLY },
{ GPG_STREAM_FLAG_EXCLUSIVE,
O_EXCL },
{ GPG_STREAM_FLAG_APPEND,
O_APPEND },
{ GPG_STREAM_FLAG_CREATE,
O_CREAT },
{ GPG_STREAM_FLAG_NONBLOCK,
O_NONBLOCK },
{ GPG_STREAM_FLAG_TRUNCATE,
O_TRUNC } };
unsigned int i = 0;
for (i = 0; i < (sizeof (flag_mappings) / sizeof (*flag_mappings)); i++)
if (flags & flag_mappings[i].gpg_stream)
open_flags |= flag_mappings[i].sys;
fd = open (file_spec->filename, open_flags, file_spec->mode);
if (fd == -1)
err = gpg_error_from_errno (errno);
}
if (! err)
{
file_handle->fd = fd;
*handle = file_handle;
}
else
{
if (file_handle)
free (file_handle);
if (fd != -1)
close (fd);
}
return err;
}
static gpg_error_t
gpg_stream_func_file_destroy (void *handle)
{
gpg_stream_handle_fd_t file_handle = handle;
gpg_error_t err = GPG_ERR_NO_ERROR;
int ret = 0;
if (file_handle)
{
ret = close (file_handle->fd);
if (ret == -1)
err = gpg_error_from_errno (errno);
free (file_handle);
}
return err;
}
gpg_stream_functions_t gpg_stream_functions_file =
{
gpg_stream_func_file_create,
gpg_stream_func_fd_read,
gpg_stream_func_fd_write,
gpg_stream_func_fd_seek,
gpg_stream_func_fd_stat,
gpg_stream_func_file_destroy
};
static gpg_error_t
gpg_stream_create_do (gpg_stream_t *stream,
void *spec,
unsigned int flags,
gpg_stream_functions_t functions)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! (1
&& (0
|| ((flags & GPG_STREAM_FLAG_READ) && functions.func_read)
|| ((flags & GPG_STREAM_FLAG_WRITE) && functions.func_write))))
err = gpg_error (GPG_ERR_INV_ARG);
else
{
buffer_functions_t buffer_fncs = { functions.func_read,
functions.func_write,
functions.func_seek,
functions.func_stat };
gpg_stream_t stream_new = NULL;
buffer_t buffer = NULL;
void *handle = NULL;
stream_new = malloc (sizeof (*stream_new));
if (! stream_new)
err = gpg_error_from_errno (errno);
if (! err)
if (functions.func_create)
err = (*functions.func_create) (&handle, spec, flags);
if (! err)
err = buffer_create (&buffer, handle, buffer_fncs);
if (! err)
{
stream_new->handle = handle;
stream_new->flags = flags;
stream_new->buffer = buffer;
stream_new->functions = functions;
*stream = stream_new;
}
else
{
if (functions.func_destroy)
(*functions.func_destroy) (handle);
if (buffer)
buffer_destroy (buffer);
}
}
return err;
}
gpg_error_t
gpg_stream_create (gpg_stream_t *stream,
void *spec,
unsigned int flags,
gpg_stream_functions_t functions)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_create_do (stream, spec, flags, functions);
return err;
}
gpg_error_t
gpg_stream_create_file (gpg_stream_t *stream,
const char *filename,
unsigned int flags)
{
gpg_stream_spec_file_t spec = { filename, GPG_STREAM_FILE_PERMISSIONS };
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_create_do (stream, &spec, flags, gpg_stream_functions_file);
return err;
}
gpg_error_t
gpg_stream_create_fd (gpg_stream_t *stream,
int fd,
unsigned int flags)
{
gpg_stream_spec_fd_t spec = { fd };
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_create_do (stream, &spec, flags, gpg_stream_functions_fd);
return err;
}
gpg_error_t
gpg_stream_destroy (gpg_stream_t stream)
{
gpg_error_t err = GPG_ERR_NO_ERROR, tmp_err = GPG_ERR_NO_ERROR;
if (stream)
{
SET_UNLESS_NONZERO (err, tmp_err, buffer_destroy (stream->buffer));
if (stream->functions.func_destroy)
SET_UNLESS_NONZERO (err, tmp_err, \
(*stream->functions.func_destroy) (stream->handle));
free (stream);
}
return err;
}
static gpg_error_t
gpg_stream_read_do (gpg_stream_t stream,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! (stream->flags & GPG_STREAM_FLAG_READ))
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
err = buffer_read (stream->buffer,
buffer, bytes_to_read, bytes_read);
return err;
}
gpg_error_t
gpg_stream_read (gpg_stream_t stream,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_read_do (stream, buffer, bytes_to_read, bytes_read);
return err;
}
static gpg_error_t
gpg_stream_write_do (gpg_stream_t stream,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! (stream->flags & GPG_STREAM_FLAG_WRITE))
err = GPG_ERR_NOT_SUPPORTED;
else
err = buffer_write (stream->buffer,
buffer, bytes_to_write, bytes_written);
return err;
}
gpg_error_t
gpg_stream_write (gpg_stream_t stream,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_write_do (stream, buffer, bytes_to_write, bytes_written);
return err;
}
static gpg_error_t
gpg_stream_read_line_do (gpg_stream_t stream,
char **line,
size_t *line_length)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! (stream->flags & GPG_STREAM_FLAG_READ))
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
{
buffer_functions_t buffer_fncs_mem = { gpg_stream_func_mem_read,
gpg_stream_func_mem_write,
gpg_stream_func_mem_seek };
void *handle = NULL;
char *line_new = NULL;
buffer_t line_buffer = NULL;
char *newline = NULL;
size_t data_size = 0;
char *data = NULL;
size_t line_size = 0;
err = gpg_stream_func_mem_create (&handle, NULL, 0);
if (! err)
err = buffer_create (&line_buffer, handle, buffer_fncs_mem);
if (! err)
do
{
err = buffer_peek (stream->buffer, &data, &data_size);
if (! err)
{
size_t bytes_written = 0;
newline = memchr (data, '\n', data_size);
if (newline)
{
/* Write until newline. */
line_size += newline - data + 1;
err = buffer_write (line_buffer, data, newline - data + 1,
&bytes_written);
if (! err)
err = buffer_skip (stream->buffer, bytes_written);
break;
}
else
{
/* Write whole block. */
line_size += data_size;
err = buffer_write (line_buffer, data, data_size,
&bytes_written);
if (! err)
err = buffer_skip (stream->buffer, bytes_written);
}
}
}
while ((! err) && data_size);
if (! err)
{
/* Complete line has been written to line_buffer. */
if (line_size)
{
err = buffer_seek (line_buffer, 0, SEEK_SET);
if (! err)
{
line_new = malloc (line_size + 1);
if (! line_new)
err = gpg_error_from_errno (errno);
}
if (! err)
{
size_t bytes_written = 0, written = 0;
while ((bytes_written < line_size) && (! err))
{
err = buffer_read (line_buffer, line_new + bytes_written,
line_size - bytes_written, &written);
bytes_written += written;
}
if (! err)
line_new[line_size] = 0;
}
}
}
if (line_buffer)
buffer_destroy (line_buffer);
if (handle)
gpg_stream_func_mem_destroy (handle);
if (! err)
{
*line = line_new;
if (line_length)
*line_length = line_size;
}
else
{
if (line_new)
free (line_new);
}
}
return err;
}
gpg_error_t
gpg_stream_read_line (gpg_stream_t stream,
char **line,
size_t *line_length)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_read_line_do (stream, line, line_length);
return err;
}
static gpg_error_t
gpg_stream_print_va_do (gpg_stream_t stream,
const char *format,
va_list ap)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
FILE *tmp_stream = NULL;
int ret = 0;
if (! (stream->flags & GPG_STREAM_FLAG_WRITE))
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
{
tmp_stream = tmpfile ();
if (! tmp_stream)
err = gpg_error_from_errno (errno);
if (! err)
{
ret = vfprintf (tmp_stream, format, ap);
if (ret == -1)
err = gpg_error_from_errno (errno);
}
if (! err)
{
ret = fseek (tmp_stream, 0, SEEK_SET);
if (ret == -1)
err = gpg_error_from_errno (errno);
}
if (! err)
{
size_t bytes_read = 0, bytes_written = 0;
char data[BUFFER_BLOCK_SIZE];
while (! err)
{
bytes_read = fread (data, 1, sizeof (data), tmp_stream);
if (ferror (tmp_stream))
err = gpg_error_from_errno (errno);
if (! err)
err = gpg_stream_write_do (stream, data,
bytes_read, &bytes_written);
if (! err)
if (feof (tmp_stream))
break;
}
}
if (tmp_stream)
fclose (tmp_stream);
}
return err;
}
gpg_error_t
gpg_stream_print_va (gpg_stream_t stream,
const char *format,
va_list ap)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_print_va_do (stream, format, ap);
return err;
}
gpg_error_t
gpg_stream_print (gpg_stream_t stream,
const char *format,
...)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
va_list ap;
va_start (ap, format);
err = gpg_stream_print_va (stream, format, ap);
va_end (ap);
return err;
}
static gpg_error_t
gpg_stream_flush_do (gpg_stream_t stream)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_flush (stream->buffer);
return err;
}
gpg_error_t
gpg_stream_flush (gpg_stream_t stream)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_flush_do (stream);
return err;
}
static gpg_error_t
gpg_stream_peek_do (gpg_stream_t stream,
char **buffer,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_peek (stream->buffer, buffer, size);
return err;
}
gpg_error_t
gpg_stream_peek (gpg_stream_t stream,
char **buffer,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_peek_do (stream, buffer, size);
return err;
}
static gpg_error_t
gpg_stream_seek_do (gpg_stream_t stream,
off_t offset,
int whence)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_seek (stream->buffer, offset, whence);
return err;
}
gpg_error_t
gpg_stream_seek (gpg_stream_t stream,
off_t offset,
int whence)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_seek_do (stream, offset, whence);
return err;
}
static gpg_error_t
gpg_stream_stat_do (gpg_stream_t stream,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_stat (stream->buffer, size);
return err;
}
gpg_error_t
gpg_stream_stat (gpg_stream_t stream,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = gpg_stream_stat_do (stream, size);
return err;
}

148
agent/gpg-stream.h Normal file
View File

@ -0,0 +1,148 @@
/* stream.h - Stream I/O layer
Copyright (C) 2004 g10 Code GmbH
This file is part of libgpg-stream.
libgpg-stream is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
libgpg-stream is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with libgpg-stream; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#ifndef GPG_STREAM_H
#define GPG_STREAM_H
#include <sys/types.h>
#include <stdarg.h>
#include <gpg-error.h>
#define STREAM_BLOCK_SIZE 1024
typedef struct gpg_stream *gpg_stream_t;
typedef gpg_error_t (*gpg_stream_func_create_t) (void **handle,
void *spec,
unsigned int flags);
typedef gpg_error_t (*gpg_stream_func_read_t) (void *handle,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read);
typedef gpg_error_t (*gpg_stream_func_write_t) (void *handle,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written);
typedef gpg_error_t (*gpg_stream_func_seek_t) (void *handle,
off_t pos,
int whence);
typedef gpg_error_t (*gpg_stream_func_stat_t) (void *handle,
size_t *size);
typedef gpg_error_t (*gpg_stream_func_destroy_t) (void *handle);
typedef struct gpg_stream_functions
{
gpg_stream_func_create_t func_create;
gpg_stream_func_read_t func_read;
gpg_stream_func_write_t func_write;
gpg_stream_func_seek_t func_seek;
gpg_stream_func_stat_t func_stat;
gpg_stream_func_destroy_t func_destroy;
} gpg_stream_functions_t;
#define GPG_STREAM_FLAG_READ (1 << 0)
#define GPG_STREAM_FLAG_WRITE (1 << 1)
#define GPG_STREAM_FLAG_EXCLUSIVE (1 << 2)
#define GPG_STREAM_FLAG_APPEND (1 << 3)
#define GPG_STREAM_FLAG_CREATE (1 << 4)
#define GPG_STREAM_FLAG_NONBLOCK (1 << 5)
#define GPG_STREAM_FLAG_TRUNCATE (1 << 6)
#define GPG_STREAM_FLAG_BINARY (1 << 7)
gpg_error_t gpg_stream_create (gpg_stream_t *stream,
void *spec,
unsigned int flags,
gpg_stream_functions_t functions);
gpg_error_t gpg_stream_create_file (gpg_stream_t *stream,
const char *filename,
unsigned int flags);
gpg_error_t gpg_stream_create_fd (gpg_stream_t *stream,
int fd,
unsigned int flags);
gpg_error_t gpg_stream_destroy (gpg_stream_t stream);
gpg_error_t gpg_stream_read (gpg_stream_t stream,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read);
gpg_error_t gpg_stream_write (gpg_stream_t stream,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written);
gpg_error_t gpg_stream_read_line (gpg_stream_t stream,
char **line,
size_t *line_length);
gpg_error_t gpg_stream_print_va (gpg_stream_t stream,
const char *format,
va_list ap);
gpg_error_t gpg_stream_print (gpg_stream_t stream,
const char *format,
...);
gpg_error_t gpg_stream_flush (gpg_stream_t stream);
gpg_error_t gpg_stream_peek (gpg_stream_t stream,
char **buffer,
size_t *size);
gpg_error_t gpg_stream_seek (gpg_stream_t stream,
off_t offset,
int whence);
gpg_error_t gpg_stream_stat (gpg_stream_t stream,
size_t *size);
typedef struct gpg_stream_spec_mem
{
char *memory;
size_t memory_size;
unsigned int grow: 1;
} gpg_stream_spec_mem_t;
extern gpg_stream_functions_t gpg_stream_functions_mem;
typedef struct gpg_stream_spec_file
{
const char *filename;
mode_t mode;
} gpg_stream_spec_file_t;
extern gpg_stream_functions_t gpg_stream_functions_file;
typedef struct gpg_stream_spec_fd
{
int fd;
} gpg_stream_spec_fd_t;
extern gpg_stream_functions_t gpg_stream_functions_fd;
#endif

View File

@ -55,18 +55,17 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash)
}
/* SIGN whatever information we have accumulated in CTRL and write it
back to OUTFP. */
/* SIGN whatever information we have accumulated in CTRL and return
the signature S-Expression. */
int
agent_pksign (CTRL ctrl, const char *desc_text, FILE *outfp, int ignore_cache)
agent_pksign_do (CTRL ctrl, const char *desc_text,
gcry_sexp_t *signature_sexp, int ignore_cache)
{
gcry_sexp_t s_skey = NULL, s_hash = NULL, s_sig = NULL;
gcry_sexp_t s_skey = NULL, s_sig = NULL;
unsigned char *shadow_info = NULL;
int rc;
char *buf = NULL;
size_t len;
unsigned int rc = 0; /* FIXME: gpg-error? */
if (!ctrl->have_keygrip)
if (! ctrl->have_keygrip)
return gpg_error (GPG_ERR_NO_SECKEY);
rc = agent_key_from_file (ctrl, desc_text, ctrl->keygrip,
@ -77,26 +76,40 @@ agent_pksign (CTRL ctrl, const char *desc_text, FILE *outfp, int ignore_cache)
goto leave;
}
if (!s_skey)
{ /* divert operation to the smartcard */
unsigned char *sigbuf;
if (! s_skey)
{
/* divert operation to the smartcard */
unsigned char *buf = NULL;
size_t len = 0;
rc = divert_pksign (ctrl,
ctrl->digest.value,
ctrl->digest.valuelen,
ctrl->digest.algo,
shadow_info, &sigbuf);
shadow_info, &buf);
if (rc)
{
log_error ("smartcard signing failed: %s\n", gpg_strerror (rc));
goto leave;
}
len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL);
len = gcry_sexp_canon_len (buf, 0, NULL, NULL);
assert (len);
buf = sigbuf;
rc = gcry_sexp_sscan (&s_sig, NULL, buf, len);
xfree (buf);
if (rc)
{
log_error ("failed to convert sigbuf returned by divert_pksign "
"into S-Exp: %s", gpg_strerror (rc));
goto leave;
}
}
else
{ /* no smartcard, but a private key */
{
/* no smartcard, but a private key */
gcry_sexp_t s_hash = NULL;
/* put the hash into a sexp */
rc = do_encode_md (ctrl->digest.value,
@ -114,6 +127,7 @@ agent_pksign (CTRL ctrl, const char *desc_text, FILE *outfp, int ignore_cache)
/* sign */
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
gcry_sexp_release (s_hash);
if (rc)
{
log_error ("signing failed: %s\n", gpg_strerror (rc));
@ -125,26 +139,47 @@ agent_pksign (CTRL ctrl, const char *desc_text, FILE *outfp, int ignore_cache)
log_debug ("result: ");
gcry_sexp_dump (s_sig);
}
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
}
leave:
if (! rc)
*signature_sexp = s_sig;
gcry_sexp_release (s_skey);
xfree (shadow_info);
return rc;
}
/* SIGN whatever information we have accumulated in CTRL and write it
back to OUTFP. */
int
agent_pksign (CTRL ctrl, const char *desc_text, FILE *outfp, int ignore_cache)
{
gcry_sexp_t s_sig = NULL;
char *buf = NULL;
size_t len = 0;
int rc = 0;
rc = agent_pksign_do (ctrl, desc_text, &s_sig, ignore_cache);
if (rc)
goto leave;
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
/* FIXME: we must make sure that no buffering takes place or we are
in full control of the buffer memory (easy to do) - should go
into assuan. */
fwrite (buf, 1, len, outfp);
leave:
gcry_sexp_release (s_skey);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_sig);
xfree (buf);
xfree (shadow_info);
return rc;
}

View File

@ -132,7 +132,7 @@ start_pinentry (CTRL ctrl)
pgmname++;
argv[0] = pgmname;
if (ctrl->display && !opt.keep_display)
if ((ctrl && ctrl->display) && !opt.keep_display)
{
argv[1] = "--display";
argv[2] = ctrl->display;
@ -169,7 +169,7 @@ start_pinentry (CTRL ctrl)
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return unlock_pinentry (map_assuan_err (rc));
if (ctrl->ttyname)
if (ctrl && ctrl->ttyname)
{
char *optstr;
if (asprintf (&optstr, "OPTION ttyname=%s", ctrl->ttyname) < 0 )
@ -180,7 +180,7 @@ start_pinentry (CTRL ctrl)
if (rc)
return unlock_pinentry (map_assuan_err (rc));
}
if (ctrl->ttytype)
if (ctrl && ctrl->ttytype)
{
char *optstr;
if (asprintf (&optstr, "OPTION ttytype=%s", ctrl->ttytype) < 0 )
@ -190,7 +190,7 @@ start_pinentry (CTRL ctrl)
if (rc)
return unlock_pinentry (map_assuan_err (rc));
}
if (ctrl->lc_ctype)
if (ctrl && ctrl->lc_ctype)
{
char *optstr;
if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 )
@ -200,7 +200,7 @@ start_pinentry (CTRL ctrl)
if (rc)
return unlock_pinentry (map_assuan_err (rc));
}
if (ctrl->lc_messages)
if (ctrl && ctrl->lc_messages)
{
char *optstr;
if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 )