diff --git a/agent/ChangeLog b/agent/ChangeLog index fb358f43b..14fbcc112 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,25 @@ +2002-05-23 Werner Koch + + * call-scd.c: Seirialized all scdaeom access when using Pth. + + * cache.c: Made the cache Pth-thread-safe. + (agent_unlock_cache_entry): New. + * findkey.c (unprotect): Unlock the returned cache value. + * command.c (cmd_get_passphrase): Ditto. + + * gpg-agent.c (main): Register pth_read/write with Assuan. + +2002-05-22 Werner Koch + + * query.c: Serialized all pinentry access when using Pth. + + * gpg-agent.c (handle_signal,start_connection_thread) + (handle_connections): New + (main): Use the new Pth stuff to allow concurrent connections. + * command.c (start_command_handler): Add new arg FD so that the + fucntion can also be used for an already connected socket. + * Makefile.am: Link with Pth. + 2002-05-14 Werner Koch * cache.c (housekeeping, agent_put_cache): Use our time() wrapper. diff --git a/agent/Makefile.am b/agent/Makefile.am index 5716519bf..6680dcc25 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -21,7 +21,7 @@ bin_PROGRAMS = gpg-agent noinst_PROGRAMS = protect-tool -AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) $(PTH_CFLAGS) LDFLAGS = @LDFLAGS@ gpg_agent_SOURCES = \ @@ -43,7 +43,7 @@ gpg_agent_SOURCES = \ gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ - ../common/libcommon.a $(LIBGCRYPT_LIBS) + ../common/libcommon.a $(LIBGCRYPT_LIBS) $(PTH_LIBS) protect_tool_SOURCES = \ protect-tool.c \ diff --git a/agent/agent.h b/agent/agent.h index 4dad5181c..44b383211 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -104,7 +104,7 @@ void agent_exit (int rc); /* also implemented in other tools */ const char *trans (const char *text); /*-- command.c --*/ -void start_command_handler (int); +void start_command_handler (int, int); /*-- findkey.c --*/ int agent_write_private_key (const unsigned char *grip, @@ -124,8 +124,8 @@ int agent_get_confirmation (const char *desc, const char *ok, /*-- cache.c --*/ int agent_put_cache (const char *key, const char *data, int ttl); -const char *agent_get_cache (const char *key); - +const char *agent_get_cache (const char *key, void **cache_id); +void agent_unlock_cache_entry (void **cache_id); /*-- pksign.c --*/ diff --git a/agent/cache.c b/agent/cache.c index ed9c8cfd9..4b18ad30f 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -40,6 +40,7 @@ struct cache_item_s { time_t created; time_t accessed; int ttl; /* max. lifetime given in seonds */ + int lockcount; struct secret_data_s *pw; char key[1]; }; @@ -87,7 +88,7 @@ housekeeping (void) /* first expire the actual data */ for (r=thecache; r; r = r->next) { - if (r->pw && r->accessed + r->ttl < current) + if (!r->lockcount && r->pw && r->accessed + r->ttl < current) { if (DBG_CACHE) log_debug (" expired `%s' (%ds after last access)\n", @@ -99,10 +100,10 @@ housekeeping (void) } /* 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 */ + that the user 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) + if (!r->lockcount && r->pw && r->created + 60*60 < current) { if (DBG_CACHE) log_debug (" expired `%s' (1h after creation)\n", r->key); @@ -118,15 +119,27 @@ housekeeping (void) { if (!r->pw && r->accessed + 60*30 < current) { - ITEM r2 = r->next; - if (DBG_CACHE) - log_debug (" removed `%s' (slot not used for 30m)\n", r->key); - xfree (r); - if (!rprev) - thecache = r2; + if (r->lockcount) + { + log_error ("can't remove unused cache entry `%s' due to" + " lockcount=%d\n", + r->key, r->lockcount); + r->accessed += 60*10; /* next error message in 10 minutes */ + rprev = r; + r = r->next; + } else - rprev->next = r2; - r = r2; + { + ITEM r2 = r->next; + if (DBG_CACHE) + log_debug (" removed `%s' (slot not used for 30m)\n", r->key); + xfree (r); + if (!rprev) + thecache = r2; + else + rprev->next = r2; + r = r2; + } } else { @@ -158,7 +171,7 @@ agent_put_cache (const char *key, const char *data, int ttl) for (r=thecache; r; r = r->next) { - if ( !strcmp (r->key, key)) + if (!r->lockcount && !strcmp (r->key, key)) break; } if (r) @@ -206,34 +219,67 @@ agent_put_cache (const char *key, const char *data, int ttl) /* Try to find an item in the cache */ const char * -agent_get_cache (const char *key) +agent_get_cache (const char *key, void **cache_id) { ITEM r; - int count = 0; if (DBG_CACHE) log_debug ("agent_get_cache `%s'...\n", key); housekeeping (); - /* FIXME: Returning pointers is not thread safe - add a reference - counter */ - for (r=thecache; r; r = r->next, count++) + /* first try to find one with no locks - this is an updated cache + entry: We might have entries with a lockcount and without a + lockcount. */ + for (r=thecache; r; r = r->next) { - if (r->pw && !strcmp (r->key, key)) + if (!r->lockcount && r->pw && !strcmp (r->key, key)) { /* put_cache does only put strings into the cache, so we don't need the lengths */ r->accessed = gnupg_get_time (); if (DBG_CACHE) log_debug ("... hit\n"); + r->lockcount++; + *cache_id = r; + return r->pw->data; + } + } + /* again, but this time get even one with a lockcount set */ + for (r=thecache; r; r = r->next) + { + if (r->pw && !strcmp (r->key, key)) + { + r->accessed = gnupg_get_time (); + if (DBG_CACHE) + log_debug ("... hit (locked)\n"); + r->lockcount++; + *cache_id = r; return r->pw->data; } } if (DBG_CACHE) log_debug ("... miss\n"); + *cache_id = NULL; return NULL; } +void +agent_unlock_cache_entry (void **cache_id) +{ + ITEM r; + for (r=thecache; r; r = r->next) + { + if (r == *cache_id) + { + if (!r->lockcount) + log_error ("trying to unlock non-locked cache entry `%s'\n", + r->key); + else + r->lockcount--; + return; + } + } +} diff --git a/agent/call-scd.c b/agent/call-scd.c index f618912f2..8b79e81cf 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -18,6 +18,12 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ +/* Fixme: For now we have serialized all access to the scdaemon which + make sense becuase the scdaemon can't handle concurrent connections + right now. We should however keep a list of connections and lock + just that connection - it migth make sense to implemtn parts of + this in Assuan.*/ + #include #include #include @@ -27,6 +33,9 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +#endif #include "agent.h" #include "../assuan/assuan.h" @@ -38,6 +47,9 @@ #endif static ASSUAN_CONTEXT scd_ctx = NULL; +#ifdef USE_GNU_PTH +static pth_mutex_t scd_lock = PTH_MUTEX_INIT; +#endif /* callback parameter for learn card */ struct learn_parm_s { @@ -60,7 +72,7 @@ struct membuf { -/* A simple implemnation of a dynamic buffer. Use init_membuf() to +/* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter @@ -122,6 +134,20 @@ get_membuf (struct membuf *mb, size_t *len) +static int +unlock_scd (int rc) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_release (&scd_lock)) + { + log_error ("failed to release the SCD lock\n"); + if (!rc) + rc = GNUPG_Internal_Error; + } +#endif + return rc; +} + /* Fork off the SCdaemon if this has not already been done */ static int start_scd (void) @@ -131,6 +157,14 @@ start_scd (void) ASSUAN_CONTEXT ctx; const char *argv[3]; +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&scd_lock, 0, NULL)) + { + log_error ("failed to acquire the SCD lock\n"); + return GNUPG_Internal_Error; + } +#endif + if (scd_ctx) return 0; /* No need to serialize things because the agent is expected to tun as a single-thread (or may be in @@ -142,7 +176,7 @@ start_scd (void) if (fflush (NULL)) { log_error ("error flushing pending output: %s\n", strerror (errno)); - return seterr (Write_Error); + return unlock_scd (seterr (Write_Error)); } /* FIXME: change the default location of the program */ @@ -163,7 +197,7 @@ start_scd (void) { log_error ("can't connect to the SCdaemon: %s\n", assuan_strerror (rc)); - return seterr (No_Scdaemon); + return unlock_scd (seterr (No_Scdaemon)); } scd_ctx = ctx; @@ -218,9 +252,9 @@ agent_card_learn (void (*kpinfo_cb)(void*, const char *), void *kpinfo_cb_arg) NULL, NULL, NULL, NULL, learn_status_cb, &parm); if (rc) - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); - return 0; + return unlock_scd (0); } @@ -274,7 +308,7 @@ agent_card_serialno (char **r_serialno) this is really SCdaemon's duty */ rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); rc = assuan_transact (scd_ctx, "SERIALNO", NULL, NULL, NULL, NULL, @@ -282,10 +316,10 @@ agent_card_serialno (char **r_serialno) if (rc) { xfree (serialno); - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); } *r_serialno = serialno; - return 0; + return unlock_scd (0); } @@ -354,7 +388,7 @@ agent_card_pksign (const char *keyid, return rc; if (indatalen*2 + 50 > DIM(line)) - return seterr (General_Error); + return unlock_scd (seterr (General_Error)); sprintf (line, "SETDATA "); p = line + strlen (line); @@ -362,7 +396,7 @@ agent_card_pksign (const char *keyid, sprintf (p, "%02X", indata[i]); rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); init_membuf (&data, 1024); inqparm.ctx = scd_ctx; @@ -377,7 +411,7 @@ agent_card_pksign (const char *keyid, if (rc) { xfree (get_membuf (&data, &len)); - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); } sigbuf = get_membuf (&data, &sigbuflen); @@ -388,7 +422,7 @@ agent_card_pksign (const char *keyid, if (!*r_buf) { xfree (*r_buf); - return GNUPG_Out_Of_Core; + return unlock_scd (GNUPG_Out_Of_Core); } p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" ); sprintf (p, "%u:", (unsigned int)sigbuflen); @@ -399,7 +433,7 @@ agent_card_pksign (const char *keyid, xfree (sigbuf); assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); - return 0; + return unlock_scd (0); } /* Decipher INDATA using the current card. Note that the returned value is */ @@ -423,7 +457,7 @@ agent_card_pkdecrypt (const char *keyid, /* FIXME: use secure memory where appropriate */ if (indatalen*2 + 50 > DIM(line)) - return seterr (General_Error); + return unlock_scd (seterr (General_Error)); sprintf (line, "SETDATA "); p = line + strlen (line); @@ -431,7 +465,7 @@ agent_card_pkdecrypt (const char *keyid, sprintf (p, "%02X", indata[i]); rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); init_membuf (&data, 1024); inqparm.ctx = scd_ctx; @@ -446,13 +480,13 @@ agent_card_pkdecrypt (const char *keyid, if (rc) { xfree (get_membuf (&data, &len)); - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) - return GNUPG_Out_Of_Core; + return unlock_scd (GNUPG_Out_Of_Core); - return 0; + return unlock_scd (0); } @@ -481,13 +515,13 @@ agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen) if (rc) { xfree (get_membuf (&data, &len)); - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) - return GNUPG_Out_Of_Core; + return unlock_scd (GNUPG_Out_Of_Core); - return 0; + return unlock_scd (0); } @@ -517,19 +551,19 @@ agent_card_readkey (const char *id, unsigned char **r_buf) if (rc) { xfree (get_membuf (&data, &len)); - return map_assuan_err (rc); + return unlock_scd (map_assuan_err (rc)); } *r_buf = get_membuf (&data, &buflen); if (!*r_buf) - return GNUPG_Out_Of_Core; + return unlock_scd (GNUPG_Out_Of_Core); if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) { xfree (*r_buf); *r_buf = NULL; - return GNUPG_Invalid_Value; + return unlock_scd (GNUPG_Invalid_Value); } - return 0; + return unlock_scd (0); } diff --git a/agent/command.c b/agent/command.c index e6f34fd39..387eef417 100644 --- a/agent/command.c +++ b/agent/command.c @@ -370,6 +370,7 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) char *response; char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL; char *p; + void *cache_marker; /* parse the stuff */ for (p=line; *p == ' '; p++) @@ -417,17 +418,18 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) desc = NULL; /* Note: we store the hexified versions in the cache. */ - pw = cacheid ? agent_get_cache (cacheid) : NULL; + pw = cacheid ? agent_get_cache (cacheid, &cache_marker) : NULL; if (pw) { assuan_begin_confidential (ctx); rc = assuan_set_okay_line (ctx, pw); + agent_unlock_cache_entry (&cache_marker); } else { - /* Note, that we only need to repalce the + characters and - should leave the other escaping in place becuase the escaped - sting is send verbatim to the pinentry which does the + /* Note, that we only need to replace the + characters and + should leave the other escaping in place because the escaped + string is send verbatim to the pinentry which does the unescaping (but not the + replacing) */ if (errtext) plus_to_blank (errtext); @@ -593,19 +595,18 @@ register_commands (ASSUAN_CONTEXT ctx) } -/* Startup the server. If LISTEN_FD is given as -1, this is a simple +/* Startup the server. If LISTEN_FD and FD is given as -1, this is a simple piper server, otherwise it is a regular server */ void -start_command_handler (int listen_fd) +start_command_handler (int listen_fd, int fd) { int rc; ASSUAN_CONTEXT ctx; struct server_control_s ctrl; memset (&ctrl, 0, sizeof ctrl); - - if (listen_fd == -1) + if (listen_fd == -1 && fd == -1) { int filedes[2]; @@ -613,10 +614,14 @@ start_command_handler (int listen_fd) filedes[1] = 1; rc = assuan_init_pipe_server (&ctx, filedes); } - else + else if (listen_fd != -1) { rc = assuan_init_socket_server (&ctx, listen_fd); } + else + { + rc = assuan_init_connected_socket_server (&ctx, fd); + } if (rc) { log_error ("failed to initialize the server: %s\n", @@ -664,5 +669,3 @@ start_command_handler (int listen_fd) assuan_deinit_server (ctx); } - - diff --git a/agent/findkey.c b/agent/findkey.c index 662a56f89..1a222ba77 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -106,10 +106,12 @@ unprotect (unsigned char **keybuf, const unsigned char *grip) /* first try to get it from the cache - if there is none or we can't unprotect it, we fall back to ask the user */ { - const char *pw = agent_get_cache (hexgrip); + void *cache_marker; + const char *pw = agent_get_cache (hexgrip, &cache_marker); if (pw) { rc = agent_unprotect (*keybuf, pw, &result, &resultlen); + agent_unlock_cache_entry (&cache_marker); if (!rc) { xfree (*keybuf); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index c39c8c4e0..ff1deece9 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -33,6 +33,9 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +#endif #include @@ -113,7 +116,13 @@ static ARGPARSE_OPTS opts[] = { }; +#ifndef USE_GNU_PTH static volatile int caught_fatal_sig = 0; +#endif /*!USE_GNU_PTH*/ + +/* flag to indicate that a shutdown was requested */ +static int shutdown_pending; + /* It is possible that we are currently running under setuid permissions */ static int maybe_setuid = 1; @@ -122,6 +131,11 @@ static int maybe_setuid = 1; static char socket_name[128]; +#ifdef USE_GNU_PTH +static void handle_connections (int listen_fd); +#endif + + static const char * my_strusage (int level) @@ -205,6 +219,7 @@ cleanup (void) } +#ifndef USE_GNU_PTH static RETSIGTYPE cleanup_sh (int sig) { @@ -226,6 +241,7 @@ cleanup_sh (int sig) #endif raise( sig ); } +#endif /*!USE_GNU_PTH*/ int main (int argc, char **argv ) @@ -265,6 +281,9 @@ main (int argc, char **argv ) } assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); +#ifdef USE_GNU_PTH + assuan_set_io_func (pth_read, pth_write); +#endif gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); @@ -447,7 +466,7 @@ main (int argc, char **argv ) if (pipe_server) { /* this is the simple pipe based server */ - start_command_handler (-1); + start_command_handler (-1, -1); } else { /* regular server mode */ @@ -574,6 +593,7 @@ main (int argc, char **argv ) /*NEVER REACHED*/ } /* end parent */ + /* this is the child */ /* detach from tty and put process into a new session */ @@ -592,6 +612,22 @@ main (int argc, char **argv ) } } + if (chdir("/")) + { + log_error ("chdir to / failed: %s\n", strerror (errno)); + exit (1); + } + + +#ifdef USE_GNU_PTH + if (!pth_init ()) + { + log_error ("failed to initialize the Pth library\n"); + exit (1); + } + signal (SIGPIPE, SIG_IGN); + handle_connections (fd); +#else /*!USE_GNU_PTH*/ /* setup signals */ { struct sigaction oact, nact; @@ -610,15 +646,8 @@ main (int argc, char **argv ) sigaction (SIGPIPE, &nact, NULL); sigaction (SIGINT, &nact, NULL); } - - if (chdir("/")) - { - log_error ("chdir to / failed: %s\n", strerror (errno)); - exit (1); - } - - start_command_handler (fd); - + start_command_handler (fd, -1); +#endif /*!USE_GNU_PTH*/ close (fd); } @@ -628,10 +657,7 @@ main (int argc, char **argv ) void agent_exit (int rc) { - #if 0 -#warning no update_random_seed_file - update_random_seed_file(); - #endif + /*FIXME: update_random_seed_file();*/ #if 0 /* at this time a bit annoying */ if (opt.debug & DBG_MEMSTAT_VALUE) @@ -647,3 +673,144 @@ agent_exit (int rc) exit (rc); } + +static void +reread_configuration (void) +{ + /* FIXME: Move parts of the option parsing to here. */ +} + + +#ifdef USE_GNU_PTH +static void +handle_signal (int signo) +{ + switch (signo) + { + case SIGHUP: + log_info ("SIGHUP received - re-reading configuration\n"); + reread_configuration (); + break; + + case SIGUSR1: + if (opt.verbose < 5) + opt.verbose++; + log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose); + break; + + case SIGUSR2: + if (opt.verbose) + opt.verbose--; + log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose ); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info ("SIGTERM received - shutting down ...\n"); + else + log_info ("SIGTERM received - still %ld running threads\n", + pth_ctrl( PTH_CTRL_GETTHREADS )); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info ("shutdown forced\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + agent_exit (0); + } + break; + + case SIGINT: + log_info ("SIGINT received - immediate shutdown\n"); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + cleanup (); + agent_exit (0); + break; + + default: + log_info ("signal %d received - no action defined\n", signo); + } +} + + +static void * +start_connection_thread (void *arg) +{ + int fd = (int)arg; + + if (opt.verbose) + log_info ("handler for fd %d started\n", fd); + start_command_handler (-1, fd); + if (opt.verbose) + log_info ("handler for fd %d terminated\n", fd); + + return NULL; +} + + +static void +handle_connections (int listen_fd) +{ + pth_attr_t tattr; + pth_event_t ev; + sigset_t sigs; + int signo; + struct sockaddr_un paddr; + socklen_t plen = sizeof( paddr ); + 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 ); + sigaddset (&sigs, SIGHUP); + sigaddset (&sigs, SIGUSR1); + sigaddset (&sigs, SIGUSR2); + sigaddset (&sigs, SIGINT); + sigaddset (&sigs, SIGTERM); + ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); + + for (;;) + { + if (shutdown_pending) + { + if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1) + break; /* ready */ + + /* Do not accept anymore connections and wait for existing + connections to terminate */ + signo = 0; + pth_wait (ev); + if (pth_event_occurred (ev) && signo) + handle_signal (signo); + continue; + } + + fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev); + if (fd == -1) + { + if (pth_event_occurred (ev)) + { + 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); + } + } + + pth_event_free (ev, PTH_FREE_ALL); + cleanup (); + log_info ("%s %s stopped\n", strusage(11), strusage(13)); +} +#endif /*USE_GNU_PTH*/ diff --git a/agent/query.c b/agent/query.c index 7a20b1498..af513b819 100644 --- a/agent/query.c +++ b/agent/query.c @@ -27,6 +27,9 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +#endif #include "agent.h" #include "../assuan/assuan.h" @@ -38,6 +41,9 @@ #endif static ASSUAN_CONTEXT entry_ctx = NULL; +#ifdef USE_GNU_PTH +static pth_mutex_t entry_lock = PTH_MUTEX_INIT; +#endif /* data to be passed to our callbacks */ struct entry_parm_s { @@ -49,7 +55,24 @@ struct entry_parm_s { -/* Fork off the pin entry if this has not already been done */ +static int +unlock_pinentry (int rc) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_release (&entry_lock)) + { + log_error ("failed to release the entry lock\n"); + if (!rc) + rc = GNUPG_Internal_Error; + } +#endif + return rc; +} + +/* Fork off the pin entry if this has not already been done. Note, + that this function must always be used to aquire the lock for the + pinentry - we will serialize _all_ pinentry calls. + */ static int start_pinentry (void) { @@ -58,10 +81,16 @@ start_pinentry (void) ASSUAN_CONTEXT ctx; const char *argv[5]; +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&entry_lock, 0, NULL)) + { + log_error ("failed to acquire the entry lock\n"); + return GNUPG_Internal_Error; + } +#endif + if (entry_ctx) - return 0; /* No need to serialize things becuase the agent is - expected to tun as a single-thread (or may be in - future using libpth) */ + return 0; if (opt.verbose) log_info ("no running PIN Entry - starting it\n"); @@ -69,7 +98,7 @@ start_pinentry (void) if (fflush (NULL)) { log_error ("error flushing pending output: %s\n", strerror (errno)); - return seterr (Write_Error); + return unlock_pinentry (seterr (Write_Error)); } /* FIXME: change the default location of the program */ @@ -80,6 +109,7 @@ start_pinentry (void) else pgmname++; + /* FIXME: We must do this thread specific */ argv[0] = pgmname; if (opt.display) { @@ -96,7 +126,7 @@ start_pinentry (void) { log_error ("can't connect to the PIN entry module: %s\n", assuan_strerror (rc)); - return seterr (No_PIN_Entry); + return unlock_pinentry (seterr (No_PIN_Entry)); } entry_ctx = ctx; @@ -107,47 +137,47 @@ start_pinentry (void) opt.no_grab? "OPTION no-grab":"OPTION grab", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); if (opt.ttyname) { char *optstr; if (asprintf (&optstr, "OPTION ttyname=%s", opt.ttyname) < 0 ) - return GNUPG_Out_Of_Core; + return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } if (opt.ttytype) { char *optstr; if (asprintf (&optstr, "OPTION ttytype=%s", opt.ttytype) < 0 ) - return GNUPG_Out_Of_Core; + return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } if (opt.lc_ctype) { char *optstr; if (asprintf (&optstr, "OPTION lc-ctype=%s", opt.lc_ctype) < 0 ) - return GNUPG_Out_Of_Core; + return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } if (opt.lc_messages) { char *optstr; if (asprintf (&optstr, "OPTION lc-messages=%s", opt.lc_messages) < 0 ) - return GNUPG_Out_Of_Core; + return unlock_pinentry (GNUPG_Out_Of_Core); rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } return 0; } @@ -213,14 +243,14 @@ agent_askpin (const char *desc_text, const char *start_err_text, line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); rc = assuan_transact (entry_ctx, pininfo->min_digits? "SETPROMPT PIN:" : "SETPROMPT Passphrase:", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++) { @@ -242,7 +272,7 @@ agent_askpin (const char *desc_text, const char *start_err_text, line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); errtext = NULL; } @@ -251,9 +281,9 @@ agent_askpin (const char *desc_text, const char *start_err_text, errtext = pininfo->min_digits? trans ("PIN too long") : trans ("Passphrase too long"); else if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); if (!errtext && !pininfo->min_digits) - return 0; /* okay, got a passphrase */ + return unlock_pinentry (0); /* okay, got a passphrase */ if (!errtext && !all_digitsp (pininfo->pin)) errtext = trans ("Invalid characters in PIN"); if (!errtext && pininfo->max_digits @@ -264,10 +294,11 @@ agent_askpin (const char *desc_text, const char *start_err_text, errtext = trans ("PIN too short"); if (!errtext) - return 0; /* okay, got a PIN */ + return unlock_pinentry (0); /* okay, got a PIN */ } - return pininfo->min_digits? GNUPG_Bad_PIN : GNUPG_Bad_Passphrase; + return unlock_pinentry (pininfo->min_digits? GNUPG_Bad_PIN + : GNUPG_Bad_Passphrase); } @@ -301,13 +332,13 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt, line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase"); line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); if (errtext) { @@ -315,28 +346,28 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt, line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } memset (&parm, 0, sizeof parm); parm.size = ASSUAN_LINELENGTH/2 - 5; parm.buffer = gcry_malloc_secure (parm.size+10); if (!parm.buffer) - return seterr (Out_Of_Core); + return unlock_pinentry (seterr (Out_Of_Core)); assuan_begin_confidential (entry_ctx); rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL); if (rc) { xfree (parm.buffer); - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1); if (!hexstring) { xfree (parm.buffer); - return seterr (Out_Of_Core); + return unlock_pinentry (seterr (Out_Of_Core)); } for (i=0, p=parm.buffer; *p; p++, i += 2) @@ -344,7 +375,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt, xfree (parm.buffer); *retpass = hexstring; - return 0; + return unlock_pinentry (0); } @@ -370,7 +401,7 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel) line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); if (ok) { @@ -378,7 +409,7 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel) line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } if (cancel) { @@ -386,11 +417,11 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel) line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL); - return map_assuan_err (rc); + return unlock_pinentry (map_assuan_err (rc)); } diff --git a/common/ChangeLog b/common/ChangeLog index 7e273d837..c9c435cff 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,7 @@ +2002-05-23 Werner Koch + + * no-pth.c, Makefile.am: Removed. + 2002-05-22 Werner Koch * mkdtemp.c: Replaced byte by unsigned char because it is no longer diff --git a/common/Makefile.am b/common/Makefile.am index 85dcfe285..d118e047d 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -32,7 +32,6 @@ libcommon_a_SOURCES = \ errors.c errors.h \ maperror.c \ sysutils.c sysutils.h \ - no-pth.c \ cryptmiss.c \ gettime.c diff --git a/common/no-pth.c b/common/no-pth.c deleted file mode 100644 index 5eaaa3f91..000000000 --- a/common/no-pth.c +++ /dev/null @@ -1,54 +0,0 @@ -/* no-pth.c - stubs to avoid linking against PTH - * 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 -#ifdef USE_GNU_PTH /*we need the stubs only in this case */ - -#include -#include -#include -#include - -#if PTH_SYSCALL_SOFT -# error this file cannot be used with PTH syscall divertion -#endif - - -ssize_t -pth_read (int a, void *b , size_t c) -{ - return read (a, b, c); -} - -ssize_t -pth_write (int a, const void *b, size_t c) -{ - return write (a, b, c); -} - -int -pth_accept (int a, struct sockaddr *b, socklen_t *c) -{ - return accept (a, b, c); -} - - - -#endif /*USE_GNU_PTH*/