From 6fbee8ab86a342abc6aa7869b709e3b272241204 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 31 Jul 1998 16:45:58 +0000 Subject: [PATCH] add coprocess facility --- NEWS | 5 ++ g10/OPTIONS | 4 + g10/g10.c | 22 +++++- g10/keyedit.c | 29 ++++--- g10/options.h | 1 + g10/signal.c | 85 ++++++++++++++++----- g10/status.c | 174 +++++++++++++++++++++++++++++++++++++++++- g10/status.h | 20 ++++- tools/shmtest.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++ util/secmem.c | 2 +- 10 files changed, 495 insertions(+), 45 deletions(-) create mode 100644 tools/shmtest.c diff --git a/NEWS b/NEWS index 4cbb5a777..2925ab450 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,11 @@ Noteworthy changes in version 0.3.3 * Owner trust values can now be changed with --edit-key (trust) + * GNUPG can now run as a coprocess; this enables sophisticated + frontends. tools/shmtest.c is a simple sample implemenation. + This needs some more work: all tty_xxx() are to be replaced + by cpr_xxx() and some changes in the display logics is needed. + * Removed options --gen-prime and --gen-random. * Removed option --add-key; use --edit-key instead. diff --git a/g10/OPTIONS b/g10/OPTIONS index 03ef351ee..2e933f57e 100644 --- a/g10/OPTIONS +++ b/g10/OPTIONS @@ -46,3 +46,7 @@ compress-sigs # Normally, compressing of signatures does not make sense; so this # is disabled for detached signatures unless this option is used. +run-as-shm-coprocess [request-locked-shm-size] +# very special :-) +# You will have to use "--status-fd" too + diff --git a/g10/g10.c b/g10/g10.c index c7d8dfcc7..54dc39c2a 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -172,7 +172,7 @@ static ARGPARSE_OPTS opts[] = { { 566, "compress-sigs",0, "@"}, { 559, "always-trust", 0, "@"}, { 562, "emulate-checksum-bug", 0, "@"}, - /*554 is unused */ + { 554, "run-as-shm-coprocess", 4, "@" }, {0} }; @@ -404,6 +404,9 @@ main( int argc, char **argv ) const char *trustdb_name = NULL; char *def_cipher_string = NULL; char *def_digest_string = NULL; + #ifdef USE_SHM_COPROCESSING + ulong requested_shm_size=0; + #endif trap_unaligned(); #ifdef IS_G10MAINT @@ -605,6 +608,13 @@ main( int argc, char **argv ) case 565: opt.do_not_export_rsa = 1; break; case 566: opt.compress_sigs = 1; break; case 554: + #ifdef USE_SHM_COPROCESSING + opt.shm_coprocess = 1; + requested_shm_size = pargs.r.ret_ulong; + #else + log_error("shared memory coprocessing is not available\n"); + #endif + break; default : errors++; pargs.err = configfp? 1:2; break; } } @@ -623,6 +633,15 @@ main( int argc, char **argv ) tty_printf("%s\n", strusage(15) ); } + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) { + #ifdef IS_G10 + init_shm_coprocessing(requested_shm_size, 1 ); + #else + init_shm_coprocessing(requested_shm_size, 0 ); + #endif + } + #endif #ifdef IS_G10 /* initialize the secure memory. */ secmem_init( 16384 ); @@ -630,7 +649,6 @@ main( int argc, char **argv ) /* Okay, we are now working under our real uid */ #endif - /*write_status( STATUS_ENTER );*/ set_debug(); diff --git a/g10/keyedit.c b/g10/keyedit.c index c137d1af7..f61916bae 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -37,6 +37,7 @@ #include "trustdb.h" #include "filter.h" #include "ttyio.h" +#include "status.h" #include "i18n.h" static void show_key_with_all_names( KBNODE keyblock, @@ -264,8 +265,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified ) tty_print_string( p, n ); tty_printf("\"\n\n"); m_free(p); - p = tty_get(_("Really sign? ")); - tty_kill_prompt(); + p = cpr_get("sign_uid.really", _("Really sign? ")); + cpr_kill_prompt(); if( !answer_is_yes(p) ) { m_free(p); continue; /* No */ @@ -398,9 +399,10 @@ delete_key( const char *username, int secret ) m_free(p); tty_printf("\n\n"); - p = tty_get(_("Delete this key from the keyring? ")); - tty_kill_prompt(); - if( secret && answer_is_yes(p)) { + p = cpr_get( secret? "delete_key.secret.really":"delete_key.really", + _("Delete this key from the keyring? ")); + cpr_kill_prompt(); + if( !cpr_enabled() && secret && answer_is_yes(p)) { /* I think it is not required to check a passphrase; if * the user is so stupid as to let others access his secret keyring * (and has no backup) - it is up him to read some very @@ -493,8 +495,8 @@ change_passphrase( KBNODE keyblock ) rc = 0; tty_printf(_( "You don't want a passphrase -" " this is probably a *bad* idea!\n\n")); - if( tty_get_answer_is_yes(_( - "Do you really want to do this? "))) + if( cpr_get_answer_is_yes("change_passwd.empty", + _("Do you really want to do this? "))) changed++; break; } @@ -628,8 +630,8 @@ keyedit_menu( const char *username, STRLIST locusr ) redisplay = 0; } m_free(answer); - answer = tty_get(_("Command> ")); - tty_kill_prompt(); + answer = cpr_get("keyedit.cmd", _("Command> ")); + cpr_kill_prompt(); trim_spaces(answer); arg_number = 0; @@ -670,12 +672,9 @@ keyedit_menu( const char *username, STRLIST locusr ) case cmdQUIT: if( !modified ) goto leave; - m_free(answer); - answer = tty_get(_("Save changes? ")); - if( !answer_is_yes(answer) ) { - m_free(answer); - answer = tty_get(_("Quit without saving? ")); - if( answer_is_yes(answer) ) + if( !cpr_get_answer_is_yes("keyedit.save",_("Save changes? ")) ) { + if( cpr_enabled() + || tty_get_answer_is_yes(_("Quit without saving? ")) ) goto leave; break; } diff --git a/g10/options.h b/g10/options.h index 0c9e17fc1..630e43f44 100644 --- a/g10/options.h +++ b/g10/options.h @@ -51,6 +51,7 @@ struct { int always_trust; int rfc1991; unsigned emulate_bugs; /* bug emulation flags EMUBUG_xxxx */ + int shm_coprocess; } opt; diff --git a/g10/signal.c b/g10/signal.c index b508e515b..be831b338 100644 --- a/g10/signal.c +++ b/g10/signal.c @@ -35,40 +35,83 @@ #include "ttyio.h" -#if 0 -static RETSIGTYPE -print_and_exit( int sig ) -{ - const char *p; +static volatile int caught_fatal_sig = 0; +static volatile int caught_sigusr1 = 0; - /* Hmm, use only safe functions (we should do an autoconf test) */ - write( 2, "\nCaught ", 8 ); +static const char * +signal_name( int signum ) +{ #if SYS_SIGLIST_DECLARED - p = sys_siglist[sig]; - write( 2, p, strlen(p) ); + return sys_siglist[signum]; #else - write( 2, "a signal", 8 ); + static char buf[20]; + sprintf( "signal %d", signum ); + return buf; #endif - write( 2, "... exiting\n", 12 ); - secmem_term(); - exit(2); /* not correct but .. */ } -#endif + +static RETSIGTYPE +got_fatal_signal( int sig ) +{ + if( caught_fatal_sig ) + raise( sig ); + caught_fatal_sig = 1; + + fprintf( stderr, "\n%s: %s caught ... exiting\n", + log_get_name(), signal_name(sig) ); + secmem_term(); + exit( 2 ); +} + + +static RETSIGTYPE +got_usr_signal( int sig ) +{ + caught_sigusr1 = 1; +} + + +static void +do_sigaction( int sig, struct sigaction *nact ) +{ + struct sigaction oact; + + sigaction( sig, NULL, &oact ); + if( oact.sa_handler != SIG_IGN ) + sigaction( sig, nact, NULL); +} void init_signals() { - #if 0 struct sigaction nact; - nact.sa_handler = print_and_exit; - sigemptyset (&nact.sa_mask); + nact.sa_handler = got_fatal_signal; + sigemptyset( &nact.sa_mask ); nact.sa_flags = 0; - sigaction( SIGINT, &nact, NULL ); - sigaction( SIGHUP, &nact, NULL ); - sigaction( SIGTERM, &nact, NULL ); - #endif + do_sigaction( SIGINT, &nact ); + do_sigaction( SIGHUP, &nact ); + do_sigaction( SIGTERM, &nact ); + do_sigaction( SIGQUIT, &nact ); + nact.sa_handler = got_usr_signal; + sigaction( SIGUSR1, &nact, NULL ); } +void +pause_on_sigusr( int which ) +{ + sigset_t mask, oldmask; + + assert( which == 1 ); + sigemptyset( &mask ); + sigaddset( &mask, SIGUSR1 ); + + sigprocmask( SIG_BLOCK, &mask, &oldmask ); + while( !caught_sigusr1 ) + sigsuspend( &oldmask ); + caught_sigusr1 = 0; + sigprocmask( SIG_UNBLOCK, &mask, NULL ); +} + diff --git a/g10/status.c b/g10/status.c index 5cdd4fd03..744f98de6 100644 --- a/g10/status.c +++ b/g10/status.c @@ -22,26 +22,45 @@ #include #include #include +#include #include +#ifdef USE_SHM_COPROCESSING + #ifdef HAVE_SYS_IPC_H + #include + #endif + #ifdef HAVE_SYS_SHM_H + #include + #endif +#endif +#include "util.h" #include "status.h" +#include "ttyio.h" +#include "options.h" +#include "main.h" static int fd = -1; +#ifdef USE_SHM_COPROCESSING + static int shm_id = -1; + static volatile char *shm_area; + static size_t shm_size; + static int shm_is_locked; +#endif /*USE_SHM_COPROCESSING*/ void -set_status_fd( int newfd ) +set_status_fd ( int newfd ) { fd = newfd; } void -write_status( int no ) +write_status ( int no ) { write_status_text( no, NULL ); } void -write_status_text( int no, const char *text) +write_status_text ( int no, const char *text) { const char *s; @@ -64,6 +83,10 @@ write_status_text( int no, const char *text) case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL\n"; break; case STATUS_TRUST_FULLY : s = "TRUST_FULLY\n"; break; case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE\n"; break; + case STATUS_SHM_INFO : s = "SHM_INFO\n"; break; + case STATUS_SHM_GET : s = "SHM_GET\n"; break; + case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL\n"; break; + case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN\n"; break; default: s = "?\n"; break; } @@ -78,3 +101,148 @@ write_status_text( int no, const char *text) write( fd, s, strlen(s) ); } + +#ifdef USE_SHM_COPROCESSING +void +init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ) +{ + char buf[100]; + + requested_shm_size = (requested_shm_size + 4095) & ~4095; + if ( requested_shm_size > 2 * 4096 ) + log_fatal("too much shared memory requested; only 8k are allowed\n"); + shm_size = 4096 /* one page for us */ + requested_shm_size; + + /* FIXME: Need other permissions ... */ + shm_id = shmget( IPC_PRIVATE, shm_size, IPC_CREAT | 0777 ); + if ( shm_id == -1 ) + log_fatal("can't get %uk of shared memory: %s\n", + (unsigned)shm_size/1024, strerror(errno)); + shm_area = shmat( shm_id, 0, 0 ); + if ( shm_area == (char*)-1 ) + log_fatal("can't attach %uk shared memory: %s\n", + (unsigned)shm_size/1024, strerror(errno)); + log_info("mapped %uk shared memory at %p, id=%d\n", + (unsigned)shm_size/1024, shm_area, shm_id ); + if( lock_mem ) { + if ( shmctl (shm_id, SHM_LOCK, 0) ) + log_info("Locking shared memory %d failed: %s\n", + shm_id, strerror(errno)); + else + shm_is_locked = 1; + } + + #ifdef IPC_RMID_DEFERRED_RELEASE + if ( shmctl ( shm_id, IPC_RMID, 0) ) + log_fatal("shmctl IPC_RMDID of %d failed: %s\n", + shm_id, strerror(errno)); + #else + #error Must add a cleanup function + #endif + + /* write info; Protocol version, id, size, locked size */ + sprintf( buf, "pv=1 pid=%d shmid=%d sz=%u lz=%u", (int)getpid(), + shm_id, (unsigned)shm_size, shm_is_locked? (unsigned)shm_size:0 ); + write_status_text( STATUS_SHM_INFO, buf ); +} + + +/**************** + * Request a string from client + * If bool, returns static string on true (do not free) or NULL for false + */ +static char * +do_shm_get( const char *keyword, int hidden, int bool ) +{ + size_t n; + byte *p; + char *string; + + if( !shm_area ) + BUG(); + + shm_area[0] = 0; /* msb of length of control block */ + shm_area[1] = 32; /* and lsb */ + shm_area[2] = 1; /* indicate that we are waiting on a reply */ + shm_area[3] = 0; /* clear data available flag */ + + write_status_text( bool? STATUS_SHM_GET_BOOL : + hidden? STATUS_SHM_GET_HIDDEN : STATUS_SHM_GET, keyword ); + + do { + pause_on_sigusr(1); + if( shm_area[0] || shm_area[1] != 32 || shm_area[2] != 1 ) + log_fatal("client modified shm control block - abort\n"); + } while( !shm_area[3] ); + shm_area[2] = 0; /* reset request flag */ + p = (byte*)shm_area+32; + n = p[0] << 8 | p[1]; + p += 2; + if( n+32+2+1 > 4095 ) + log_fatal("client returns too large data (%u bytes)\n", (unsigned)n ); + + if( bool ) + return p[0]? "" : NULL; + + string = hidden? m_alloc_secure( n+1 ) : m_alloc( n+1 ); + memcpy(string, p, n ); + string[n] = 0; /* make sure it is a string */ + if( hidden ) /* invalidate the memory */ + memset( p, 0, n ); + + return string; +} + +#endif /* USE_SHM_COPROCESSING */ + + +int +cpr_enabled() +{ + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return 1; + #endif + return 0; +} + +char * +cpr_get( const char *keyword, const char *prompt ) +{ + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return do_shm_get( keyword, 0, 0 ); + #endif + return tty_get( prompt ); +} + +char * +cpr_get_hidden( const char *keyword, const char *prompt ) +{ + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return do_shm_get( keyword, 1, 0 ); + #endif + return tty_get_hidden( prompt ); +} + +void +cpr_kill_prompt(void) +{ + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return; + #endif + return tty_kill_prompt(); +} + +int +cpr_get_answer_is_yes( const char *keyword, const char *prompt ) +{ + #ifdef USE_SHM_COPROCESSING + if( opt.shm_coprocess ) + return !!do_shm_get( keyword, 0, 1 ); + #endif + return tty_get_answer_is_yes( prompt ); +} + diff --git a/g10/status.h b/g10/status.h index d6ed773a0..f49e593a4 100644 --- a/g10/status.h +++ b/g10/status.h @@ -42,12 +42,26 @@ #define STATUS_TRUST_FULLY 14 #define STATUS_TRUST_ULTIMATE 15 +#define STATUS_SHM_INFO 16 +#define STATUS_SHM_GET 17 +#define STATUS_SHM_GET_BOOL 18 +#define STATUS_SHM_GET_HIDDEN 19 /*-- status.c --*/ -void set_status_fd( int fd ); -void write_status( int no ); -void write_status_text( int no, const char *text); +void set_status_fd ( int fd ); +void write_status ( int no ); +void write_status_text ( int no, const char *text ); + +#ifdef USE_SHM_COPROCESSING + void init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ); +#endif /*USE_SHM_COPROCESSING*/ + +int cpr_enabled(void); +char *cpr_get( const char *keyword, const char *prompt ); +char *cpr_get_hidden( const char *keyword, const char *prompt ); +void cpr_kill_prompt(void); +int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); #endif /*G10_STATUS_H*/ diff --git a/tools/shmtest.c b/tools/shmtest.c new file mode 100644 index 000000000..3f56669d6 --- /dev/null +++ b/tools/shmtest.c @@ -0,0 +1,198 @@ +/* shmtest.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IPC_H + #include +#endif +#ifdef HAVE_SYS_SHM_H + #include +#endif +#include "util.h" +#include "ttyio.h" +#include "i18n.h" + + +static int serverpid = -1; + +static void +my_usage(void) +{ + fprintf(stderr, "usage: shmtest gpg-command-line\n"); + exit(1); +} + +const char * +strusage( int level ) +{ + return default_strusage(level); +} + +static void +i18n_init(void) +{ + #ifdef ENABLE_NLS + #ifdef HAVE_LC_MESSAGES + setlocale( LC_MESSAGES, "" ); + #else + setlocale( LC_ALL, "" ); + #endif + bindtextdomain( PACKAGE, G10_LOCALEDIR ); + textdomain( PACKAGE ); + #endif +} + + +static void +do_get_string( int mode, const char *keyword, byte *area, size_t areasize ) +{ + size_t n, len; + char *p=NULL; + int yes; + + n = area[0] << 8 | area[1]; + /* fixme: do some sanity checks here */ + if( mode == 1 ) + p = tty_get( keyword ); + else if( mode == 3 ) + p = tty_get_hidden( keyword ); + else + yes = tty_get_answer_is_yes( keyword ); + if( p ) { + len = strlen(p); + memcpy( area+n+2, p, len ); + area[n] = len >> 8; + area[n+1] = len; + m_free(p); + } + else { /* bool */ + area[n] = 0; + area[n+1] = 1; + area[n+2] = yes; + } + area[3] = 1; /* we should better use a semaphore */ + kill( serverpid, SIGUSR1 ); +} + + + +int +main(int argc, char **argv) +{ + void *area = NULL; + size_t areasize = 4096; + int shm_id = -1; + FILE *fp; + char buf[200]; + char *p, *p2; + size_t n; + int i; + + log_set_name("shmtest"); + i18n_init(); + #ifndef USE_SHM_COPROCESSING + log_info("SHM_COPRPOCESSING is not available\n"); + #else + if( argc < 1 ) + my_usage(); + + #if 0 + shm_ID = atoi( argv[1] ); + pool = shmat( shm_ID, 0, 0 ); + if( pool == (void*)-1 ) + log_fatal("shmat of %d failed: %s\n", shm_ID, strerror(errno)); + log_info("attached to %p id=%d\n", pool, shm_ID ); + getchar(); + #endif + + for(n=0,i=1; i < argc; i++ ) + n += strlen(argv[i]) + 1; + p = m_alloc( 100 + n ); + strcpy( p, "../g10/gpg --status-fd 1 --run-as-shm-coprocess 0"); + for(i=1; i < argc; i++ ) { + strcat(p, " " ); + strcat(p, argv[i] ); + } + + fp = popen( p, "r" ); + m_free( p ); + if( !fp ) + log_error("popen failed: %s\n", strerror(errno)); + + while ( fgets (buf, sizeof (buf) - 1, fp ) != NULL ) { + size_t len = strlen(buf); + if( len >= 9 && !memcmp( buf, "[GNUPG:] ", 9 ) ) { + int word=0; + int is_info = 0, is_get = 0; + + for( p = strtok(buf+9, " \n"); p ; p = strtok(NULL, " \n")) { + word++; + if( word==1 && !strcmp(p,"SHM_INFO") ) { + if( !area ) + is_info=1; + else + log_error("duplicate SHM_INFO ignored\n" ); + } + else if( is_info && (p2 = strchr(p, '=' )) ) { + int val; + *p2++ = 0; + val = atoi(p2); /* should be atou() for some values */ + if( !strcmp(p, "pv" ) ) { + if( atoi(p2) != 1 ) + log_fatal("invalid protocol version %d\n", val ); + is_info = 2; + } + else if( !strcmp(p, "pid" ) ) + serverpid = val; + else if( !strcmp(p, "shmid" ) ) + shm_id = val; + } + else if( word == 1 && !strcmp(p,"SHM_GET") ) + is_get = 1; + else if( word == 1 && !strcmp(p,"SHM_GET_BOOL") ) + is_get = 2; + else if( word == 1 && !strcmp(p,"SHM_GET_HIDDEN") ) + is_get = 3; + else if( word == 2 && is_get ) { + do_get_string( is_get, p, area, areasize ); + break; + } + else if( word == 1 ) + log_info("Status: %s\n", p); + } + if( is_info ) { + if( is_info < 2 ) + log_fatal("SHM info without protocol version\n"); + if( serverpid == -1 ) + log_fatal("SHM info without server's pid\n"); + if( shm_id == -1 ) + log_fatal("SHM info without id\n"); + log_info("Shared memory info: server=%d shm_id=%d\n", + serverpid, shm_id); + area = shmat( shm_id, 0, 0 ); + if( area == (void*)-1 ) + log_fatal("attach to shared memory failed: %s\n", + strerror(errno)); + } + } + else + fputs (buf, stdout); + } + + + if( pclose(fp) ) + log_error("pclose failed\n"); + + return 0; + #endif +} + + + diff --git a/util/secmem.c b/util/secmem.c index d12621324..583c96201 100644 --- a/util/secmem.c +++ b/util/secmem.c @@ -271,7 +271,7 @@ secmem_free( void *a ) int m_is_secure( const void *p ) { - return p >= pool && p < ((char*)pool+poolsize); + return p >= pool && p < (void*)((char*)pool+poolsize); } void