diff --git a/agent/Makefile.am b/agent/Makefile.am index 222a68b98..f4b6bf8c3 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -24,7 +24,9 @@ INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/intl LDFLAGS = @LDFLAGS@ gpg_agent_SOURCES = \ - gpg-agent.c + gpg-agent.c agent.h \ + command.c \ + pksign.c gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ ../../libgcrypt/src/.libs/libgcrypt.so.1 diff --git a/agent/agent.h b/agent/agent.h new file mode 100644 index 000000000..e689b8e56 --- /dev/null +++ b/agent/agent.h @@ -0,0 +1,78 @@ +/* agent.h - Global definitions for the agent + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef AGENT_H +#define AGENT_H + +#include "../common/util.h" +#include "../common/errors.h" + +#define MAX_DIGEST_LEN 24 + +/* A large struct name "opt" to keep global flags */ +struct { + unsigned int debug; /* debug flags (DBG_foo_VALUE) */ + int verbose; /* verbosity level */ + int quiet; /* be as quiet as possible */ + int dry_run; /* don't change any persistent data */ + const char *homedir; /* configuration directory name */ +} opt; + + +#define DBG_COMMAND_VALUE 1 /* debug commands i/o */ +#define DBG_MPI_VALUE 2 /* debug mpi details */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ + +#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) + +struct server_local_s; + +struct server_control_s { + struct server_local_s *server_local; + struct { + unsigned char value[MAX_DIGEST_LEN]; + int valuelen; + } digest; + + +}; +typedef struct server_control_s *CTRL; + +/*-- gpg-agent.c --*/ +void agent_exit (int rc); + + +/*-- command.c --*/ +void start_command_handler (void); + +/*-- pksign.c --*/ +int agent_pksign (CTRL ctrl, FILE *outfp); + + + +#endif /*AGENT_H*/ diff --git a/agent/command.c b/agent/command.c new file mode 100644 index 000000000..62950b365 --- /dev/null +++ b/agent/command.c @@ -0,0 +1,229 @@ +/* command.c - gpg-agent command handler + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* FIXME: we should not use the default assuan buffering but setup + some buffering in secure mempory to protect session keys etc. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include "../assuan/assuan.h" + +#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) +#define digitp(a) ((a) >= '0' && (a) <= '9') +#define hexdigitp(a) (digitp (a) \ + || ((a) >= 'A' && (a) <= 'F') \ + || ((a) >= 'a' && (a) <= 'f')) +#define atoi_1(p) (*(p) - '0' ) +#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) +/* assumes ASCII and pre-checked values */ +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + +/* Data used to associate an Assuan context with local server data */ +struct server_local_s { + ASSUAN_CONTEXT assuan_ctx; + int message_fd; +}; + +/* SETHASH + + The client can use this command to tell the server about the data + (which usually is a hash) to be signed. */ +static int +cmd_sethash (ASSUAN_CONTEXT ctx, char *line) +{ + int n; + char *p; + CTRL ctrl = assuan_get_pointer (ctx); + unsigned char *buf; + + /* parse the hash value */ + for (p=line,n=0; hexdigitp (*p); p++, n++) + ; + if (*p) + return set_error (Parameter_Error, "invalid hexstring"); + if ((n&1)) + return set_error (Parameter_Error, "odd number of digits"); + n /= 2; + if (n != 16 && n != 20 && n != 24 && n != 32) + return set_error (Parameter_Error, "unsupported length of hash"); + if (n > MAX_DIGEST_LEN) + return set_error (Parameter_Error, "hash value to long"); + + buf = ctrl->digest.value; + ctrl->digest.valuelen = n; + for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++) + buf[n] = xtoi_2 (p); + for (; n < ctrl->digest.valuelen; n++) + buf[n] = 0; + return 0; +} + + +/* PKSIGN + + Perform the actual sign operation. Neither input nor output are + sensitive to to eavesdropping */ +static int +cmd_pksign (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + CTRL ctrl = assuan_get_pointer (ctx); + + /* fixme: check that all required data is available */ + rc = agent_pksign (ctrl, assuan_get_data_fp (ctx)); + /* fixme: return an error */ + return 0; +} + + +/* MESSAGE FD= + + Set the file descriptor to read a message which is used with + detached signatures */ +static int +cmd_message (ASSUAN_CONTEXT ctx, char *line) +{ +#if 0 + char *endp; + int fd; + CTRL ctrl = assuan_get_pointer (ctx); + + if (strncmp (line, "FD=", 3)) + return set_error (Syntax_Error, "FD= expected"); + line += 3; + if (!digitp (*line)) + return set_error (Syntax_Error, "number required"); + fd = strtoul (line, &endp, 10); + if (*endp) + return set_error (Syntax_Error, "garbage found"); + if (fd == -1) + return set_error (No_Input, NULL); + + ctrl->server_local->message_fd = fd; + return 0; +#endif + return set_error (Not_Implemented, NULL); +} + + + +/* Tell the assuan library about our commands */ +static int +register_commands (ASSUAN_CONTEXT ctx) +{ + static struct { + const char *name; + int cmd_id; + int (*handler)(ASSUAN_CONTEXT, char *line); + } table[] = { + { "SETHASH", 0, cmd_sethash }, + { "PKSIGN", 0, cmd_pksign }, + { "", ASSUAN_CMD_INPUT, NULL }, + { "", ASSUAN_CMD_OUTPUT, NULL }, + { "MESSAGE", 0, cmd_message }, + { NULL } + }; + int i, j, rc; + + for (i=j=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, + table[i].cmd_id? table[i].cmd_id + : (ASSUAN_CMD_USER + j++), + table[i].name, table[i].handler); + if (rc) + return rc; + } + return 0; +} + + +/* Startup the server */ +void +start_command_handler (void) +{ + int rc; + int filedes[2]; + ASSUAN_CONTEXT ctx; + struct server_control_s ctrl; + + memset (&ctrl, 0, sizeof ctrl); + + /* For now we use a simple pipe based server so that we can work + from scripts. We will later add options to run as a daemon and + wait for requests on a Unix domain socket */ + filedes[0] = 0; + filedes[1] = 1; + rc = assuan_init_pipe_server (&ctx, filedes); + if (rc) + { + log_error ("failed to initialize the server: %s\n", + assuan_strerror(rc)); + agent_exit (2); + } + rc = register_commands (ctx); + if (rc) + { + log_error ("failed to the register commands with Assuan: %s\n", + assuan_strerror(rc)); + agent_exit (2); + } + + assuan_set_pointer (ctx, &ctrl); + ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); + ctrl.server_local->assuan_ctx = ctx; + ctrl.server_local->message_fd = -1; + + log_info ("Assuan started\n"); + for (;;) + { + rc = assuan_accept (ctx); + if (rc == -1) + { + log_info ("Assuan terminated\n"); + break; + } + else if (rc) + { + log_info ("Assuan accept problem: %s\n", assuan_strerror (rc)); + break; + } + + rc = assuan_process (ctx); + if (rc) + { + log_info ("Assuan processing failed: %s\n", assuan_strerror (rc)); + continue; + } + } + + + assuan_deinit_pipe_server (ctx); +} + diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index b0c6c95a5..421578914 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -18,16 +18,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -/* - * How to use the client mode: - * printf '\0\0\0\002AAAAAAAAAAAAAAAAAAAAAPlease enter the passphrase for\n - * key 0x12345678\nJoe Hacker \n' - * | ./gpg-agent --client | od - * Or use a builtin command to shut the agent down. - * ./gpg-agent --shutdown | od - * - */ - #include #include @@ -43,9 +33,12 @@ #include -#include "util.h" +#include "agent.h" #include "../assuan/assuan.h" /* malloc hooks */ -#include "i18n.h" + + +#define N_(a) a +#define _(a) a enum cmd_and_opt_values @@ -72,6 +65,7 @@ enum cmd_and_opt_values aTest }; + static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, N_("@Options:\n ") }, @@ -95,17 +89,6 @@ static ARGPARSE_OPTS opts[] = { }; -static struct { - const char *homedir; - int client; - int verbose; - int quiet; - unsigned int debug; - int nodetach; - int grab; - const char *logfile; - int csh_style; -} opt; typedef struct { int used; @@ -231,8 +214,13 @@ main (int argc, char **argv ) int greeting = 0; int nogreeting = 0; int server_mode = 0; + int client = 0; int do_shutdown = 0; int do_flush = 0; + int nodetach = 0; + int grab = 0; + int csh_style = 0; + char *logfile = NULL; set_strusage( my_strusage ); /* log_set_name ("gpg-agent"); */ @@ -252,7 +240,7 @@ main (int argc, char **argv ) shell = getenv("SHELL"); if (shell && strlen(shell) >= 3 && !strcmp(shell+strlen(shell)-3, "csh") ) - opt.csh_style = 1; + csh_style = 1; opt.homedir = getenv("GNUPGHOME"); if (!opt.homedir || !*opt.homedir) @@ -263,7 +251,7 @@ main (int argc, char **argv ) opt.homedir = "~/.gnupg-test"; #endif } - opt.grab = 1; + grab = 1; /* check whether we have a config file on the commandline */ orig_argc = argc; @@ -345,14 +333,14 @@ main (int argc, char **argv ) case oNoVerbose: opt.verbose = 0; break; case oNoOptions: break; /* no-options */ case oHomedir: opt.homedir = pargs.r.ret_str; break; - case oNoDetach: opt.nodetach = 1; break; - case oNoGrab: opt.grab = 0; break; - case oClient: opt.client = 1; break; - case oShutdown: opt.client = 1; do_shutdown = 1; break; - case oFlush: opt.client = 1; do_flush = 1; break; - case oLogFile: opt.logfile = pargs.r.ret_str; break; - case oCsh: opt.csh_style = 1; break; - case oSh: opt.csh_style = 0; break; + case oNoDetach: nodetach = 1; break; + case oNoGrab: grab = 0; break; + case oClient: client = 1; break; + case oShutdown: client = 1; do_shutdown = 1; break; + case oFlush: client = 1; do_flush = 1; break; + case oLogFile: logfile = pargs.r.ret_str; break; + case oCsh: csh_style = 1; break; + case oSh: csh_style = 0; break; case oServer: server_mode = 1; break; default : pargs.err = configfp? 1:2; break; @@ -390,7 +378,7 @@ main (int argc, char **argv ) exit (1); } - if (opt.client) + if (client) { /* a client for testing this agent */ #if 0 /* FIXME: We are going to use assuan here */ int fd; @@ -444,6 +432,10 @@ main (int argc, char **argv ) close (fd ); #endif } + else if (server_mode) + { /* for now this is the simple pipe based server */ + start_command_handler (); + } else { /* regular server mode */ int listen_fd; @@ -495,7 +487,7 @@ main (int argc, char **argv ) } /* print the environment string, so that the caller can use eval to set it */ - if (opt.csh_style) + if (csh_style) { *strchr (infostr, '=') = ' '; printf ( "setenv %s\n", infostr); @@ -510,7 +502,7 @@ main (int argc, char **argv ) if ( (opt.debug & 1) ) sleep( 20 ); /* give us some time to attach gdb to the child */ - if (opt.logfile) + if (logfile) { /* FIXME:log_set_logfile (opt.logfile, -1);*/ } @@ -522,7 +514,7 @@ main (int argc, char **argv ) exit (1); } - if ( !opt.nodetach ) + if ( !nodetach ) { for (i=0 ; i <= 2; i++ ) { @@ -589,6 +581,28 @@ main (int argc, char **argv ) return 0; } +void +agent_exit (int rc) +{ + #if 0 +#warning no update_random_seed_file + update_random_seed_file(); + #endif +#if 0 + /* at this time a bit annoying */ + if (opt.debug & DBG_MEMSTAT_VALUE) + { + gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); + gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + } + if (opt.debug) + gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); +#endif + gcry_control (GCRYCTL_TERM_SECMEM ); + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} + static int start_listening (const char *name) diff --git a/agent/pksign.c b/agent/pksign.c new file mode 100644 index 000000000..69093a07c --- /dev/null +++ b/agent/pksign.c @@ -0,0 +1,39 @@ +/* pksign.c - public key signing (well, acually using a secret key) + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" + + +/* SIGN whatever information we have accumulated in CTRL and write it back to + OUTFP. */ +int +agent_pksign (CTRL ctrl, FILE *outfp) +{ + + return -1; +}