diff --git a/agent/ChangeLog b/agent/ChangeLog index 95285c347..536aed833 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,5 +1,11 @@ 2002-01-19 Werner Koch + * gpg-agent.c (main): Disable core dumps. + + * cache.c: New. + * command.c (cmd_get_passphrase): Use the cache. + (cmd_clear_passphrase): Ditto. + * gpg-agent.c: Removed unused cruft and implement the socket based server. (my_strusage): Take bug report address from configure.ac. diff --git a/agent/Makefile.am b/agent/Makefile.am index 68ee4c791..013862e48 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -27,6 +27,7 @@ gpg_agent_SOURCES = \ gpg-agent.c agent.h \ command.c \ query.c \ + cache.c \ trans.c \ findkey.c \ pksign.c \ diff --git a/agent/agent.h b/agent/agent.h index ad0a6e8b7..ca70150c8 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -100,6 +100,12 @@ int agent_get_passphrase (char **retpass, const char *desc, const char *prompt, const char *errtext); +/*-- cache.c --*/ +int agent_put_cache (const char *key, const char *data, int ttl); +const char *agent_get_cache (const char *key); + + + /*-- pksign.c --*/ int agent_pksign (CTRL ctrl, FILE *outfp); diff --git a/agent/cache.c b/agent/cache.c new file mode 100644 index 000000000..96213712d --- /dev/null +++ b/agent/cache.c @@ -0,0 +1,220 @@ +/* cache.c - keep a cache of passphrases + * Copyright (C) 2002 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 "agent.h" + +struct secret_data_s { + int totallen; /* this includes the padding */ + int datalen; /* actual data length */ + char data[1]; +}; + +typedef struct cache_item_s *ITEM; +struct cache_item_s { + ITEM next; + time_t created; + time_t accessed; + int ttl; /* max. lifetime given in seonds */ + struct secret_data_s *pw; + char key[1]; +}; + + +static ITEM thecache; + + +static void +release_data (struct secret_data_s *data) +{ + xfree (data); +} + +static struct secret_data_s * +new_data (const void *data, size_t length) +{ + struct secret_data_s *d; + int total; + + /* we pad the data to 32 bytes so that it get more complicated + finding something out by watching allocation patterns. This is + usally not possible but we better assume nothing about our + secure storage provider*/ + total = length + 32 - (length % 32); + + d = gcry_malloc_secure (sizeof d + total - 1); + if (d) + { + d->totallen = total; + d->datalen = length; + memcpy (d->data, data, length); + } + return d; +} + + +/* check whether there are items to expire */ +static void +housekeeping (void) +{ + ITEM r, rprev; + time_t current = time (NULL); + + /* first expire the actual data */ + for (r=thecache; r; r = r->next) + { + if (r->pw && r->accessed + r->ttl < current) + { + release_data (r->pw); + r->pw = NULL; + r->accessed = current; + } + } + + /* second, make sure that we also remove them based on the created stamp so + that the used has to enter it from time to time. We do this every hour */ + for (r=thecache; r; r = r->next) + { + if (r->pw && r->created + 60*60 < current) + { + release_data (r->pw); + r->pw = NULL; + r->accessed = current; + } + } + + /* third, make sure that we don't have too many items in the list. + Expire old and unused entries after 30 minutes */ + for (rprev=NULL, r=thecache; r; ) + { + if (!r->pw && r->accessed + 60*30 < current) + { + ITEM r2 = r->next; + xfree (r); + if (!rprev) + thecache = r2; + else + rprev = r2; + r = r2; + } + else + { + rprev = r; + r = r->next; + } + } +} + + + +/* Store DATA of length DATALEN in the cache under KEY and mark it + with a maxiumum lifetime of TTL seconds. If tehre is already data + under this key, it will be replaced. Using a DATA of NULL deletes + the entry */ +int +agent_put_cache (const char *key, const char *data, int ttl) +{ + ITEM r; + + housekeeping (); + + if (ttl < 1) + ttl = 60*5; /* default is 5 minutes */ + + for (r=thecache; r; r = r->next) + { + if ( !strcmp (r->key, key)) + break; + } + if (r) + { /* replace */ + if (r->pw) + { + release_data (r->pw); + r->pw = NULL; + } + if (data) + { + r->pw = new_data (data, strlen (data)+1); + if (!r->pw) + log_error ("out of core while allocating new cache item\n"); + } + } + else if (data) + { /* simply insert */ + r = xtrycalloc (1, sizeof *r + strlen (key)); + if (!r) + log_error ("out of core while allocating new cache control\n"); + else + { + strcpy (r->key, key); + r->created = r->accessed = time (NULL); + r->ttl = ttl; + r->pw = new_data (data, strlen (data)+1); + if (!r->pw) + { + log_error ("out of core while allocating new cache item\n"); + xfree (r); + } + else + { + r->next = thecache; + thecache = r; + } + } + } + return 0; +} + + +/* Try to find an item in the cache */ +const char * +agent_get_cache (const char *key) +{ + ITEM r; + int count = 0; + + housekeeping (); + + /* FIXME: Returning pointers is not thread safe - add a referencense + counter */ + for (r=thecache; r; r = r->next, count++) + { + if (r->pw && !strcmp (r->key, key)) + { + /* put_cache does onlu put strings into the cache, so we + don't need the lengths */ + r->accessed = time (NULL); + return r->pw->data; + } + } + + return NULL; +} + + + diff --git a/agent/command.c b/agent/command.c index bde3835ab..dae6c34e6 100644 --- a/agent/command.c +++ b/agent/command.c @@ -281,20 +281,74 @@ static int cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) { int rc; + const char *pw; char *response; - char *desc, *prompt, *errtext; + char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL; + char *p; - /* FIXME: Parse that stuff */ - desc = "We need a passphrase"; - prompt = NULL; - errtext = NULL; + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + cacheid = p; + p = strchr (cacheid, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + errtext = p; + p = strchr (errtext, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + prompt = p; + p = strchr (prompt, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + desc = p; + p = strchr (desc, ' '); + if (p) + *p = 0; /* ignore garbage */ + } + } + } + if (!cacheid || !*cacheid || strlen (cacheid) > 50) + return set_error (Parameter_Error, "invalid length of cacheID"); + if (!desc) + return set_error (Parameter_Error, "no description given"); - rc = agent_get_passphrase (&response, desc, prompt, errtext); - if (!rc) + if (!strcmp (cacheid, "X")) + cacheid = NULL; + if (!strcmp (errtext, "X")) + errtext = NULL; + if (!strcmp (prompt, "X")) + prompt = NULL; + if (!strcmp (desc, "X")) + desc = NULL; + + /* Note: we store the hexified versions in the cache. */ + pw = cacheid ? agent_get_cache (cacheid) : NULL; + if (pw) { assuan_begin_confidential (ctx); - rc = assuan_set_okay_line (ctx, response); - xfree (response); + rc = assuan_set_okay_line (ctx, pw); + } + else + { + rc = agent_get_passphrase (&response, desc, prompt, errtext); + if (!rc) + { + if (cacheid) + agent_put_cache (cacheid, response, 0); + assuan_begin_confidential (ctx); + rc = assuan_set_okay_line (ctx, response); + xfree (response); + } } return map_to_assuan_status (rc); @@ -310,12 +364,21 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) static int cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line) { - int rc; + char *cacheid = NULL; + char *p; - /* fixme: no caching yet. so return with OK */ - rc = 0; + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + cacheid = p; + p = strchr (cacheid, ' '); + if (p) + *p = 0; /* ignore garbage */ + if (!cacheid || !*cacheid || strlen (cacheid) > 50) + return set_error (Parameter_Error, "invalid length of cacheID"); - return map_to_assuan_status (rc); + agent_put_cache (cacheid, NULL, 0); + return 0; } diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 6dca9631b..1c3efee0b 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -40,6 +40,8 @@ #include "agent.h" #include "../assuan/assuan.h" /* malloc hooks */ +#include "sysutils.h" + #define N_(a) a #define _(a) a @@ -252,7 +254,7 @@ main (int argc, char **argv ) gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); - may_coredump = 0/* FIXME: disable_core_dumps()*/; + may_coredump = disable_core_dumps (); shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )