diff --git a/agent/Makefile.am b/agent/Makefile.am index f4b6bf8c3..a8172bece 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -29,6 +29,7 @@ gpg_agent_SOURCES = \ pksign.c gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ + ../common/libcommon.a \ ../../libgcrypt/src/.libs/libgcrypt.so.1 diff --git a/agent/agent.h b/agent/agent.h index e689b8e56..95bfa080e 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -55,10 +55,11 @@ struct server_local_s; struct server_control_s { struct server_local_s *server_local; struct { + int algo; unsigned char value[MAX_DIGEST_LEN]; int valuelen; } digest; - + char keygrip[20]; }; typedef struct server_control_s *CTRL; diff --git a/agent/command.c b/agent/command.c index 62950b365..80e13182d 100644 --- a/agent/command.c +++ b/agent/command.c @@ -44,13 +44,55 @@ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#if MAX_DIGEST_LEN < 20 +#error MAX_DIGEST_LEN shorter than keygrip +#endif + /* Data used to associate an Assuan context with local server data */ struct server_local_s { ASSUAN_CONTEXT assuan_ctx; int message_fd; }; -/* SETHASH + +static void +reset_notify (ASSUAN_CONTEXT ctx) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + memset (ctrl->keygrip, 0, 20); + ctrl->digest.valuelen = 0; +} + +/* SIGKEY + + Set the key used for a sign operation */ +static int +cmd_sigkey (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 != 20) + return set_error (Parameter_Error, "invalid length of keygrip"); + + buf = ctrl->keygrip; + for (p=line, n=0; n < 20; p += 2, n++) + buf[n] = xtoi_2 (p); + return 0; +} + +/* SETHASH The client can use this command to tell the server about the data (which usually is a hash) to be signed. */ @@ -61,6 +103,16 @@ cmd_sethash (ASSUAN_CONTEXT ctx, char *line) char *p; CTRL ctrl = assuan_get_pointer (ctx); unsigned char *buf; + char *endp; + int algo; + + /* parse the algo number and check it */ + algo = (int)strtoul (line, &endp, 10); + for (line = endp; *line == ' ' || *line == '\t'; line++) + ; + if (!algo || gcry_md_test_algo (algo)) + return set_error (Unsupported_Algorithm, NULL); + ctrl->digest.algo = algo; /* parse the hash value */ for (p=line,n=0; hexdigitp (*p); p++, n++) @@ -102,36 +154,6 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) } -/* 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 @@ -142,11 +164,11 @@ register_commands (ASSUAN_CONTEXT ctx) int cmd_id; int (*handler)(ASSUAN_CONTEXT, char *line); } table[] = { + { "SIGKEY", 0, cmd_sigkey }, { "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; @@ -160,6 +182,7 @@ register_commands (ASSUAN_CONTEXT ctx) if (rc) return rc; } + assuan_register_reset_notify (ctx, reset_notify); return 0; } diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 421578914..71e909057 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -102,9 +102,13 @@ typedef struct { #define MAX_CACHE_AGE 1000 /* should fit into an integer */ static volatile int caught_fatal_sig = 0; static volatile int shut_me_down = 0; -static CACHE_SLOT the_cache[MAX_CACHE_ENTRIES]; +/* static CACHE_SLOT the_cache[MAX_CACHE_ENTRIES]; */ static char *socket_name = NULL; +/* It is possible that we are currently running under setuid permissions */ +static int maybe_setuid = 1; + + #define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) #define u32tobuf( p, a ) do { \ @@ -116,10 +120,7 @@ static char *socket_name = NULL; static int start_listening ( const char *name ); -static int writen ( int fd, const void *buf, size_t nbytes ); -static int readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ); -static void process_request ( int fd ); static const char * @@ -204,6 +205,7 @@ main (int argc, char **argv ) { ARGPARSE_ARGS pargs; int orig_argc; + int may_coredump; char **orig_argv; FILE *configfp = NULL; char *configname = NULL; @@ -223,6 +225,10 @@ main (int argc, char **argv ) char *logfile = NULL; set_strusage( my_strusage ); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to INIT_SECMEM() + somewhere after the option parsing */ /* log_set_name ("gpg-agent"); */ srand (time (NULL)); /* the about dialog uses rand() */ i18n_init (); @@ -236,10 +242,13 @@ main (int argc, char **argv ) } assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = 0/* FIXME: disable_core_dumps()*/; - shell = getenv("SHELL"); - if (shell && strlen(shell) >= 3 && !strcmp(shell+strlen(shell)-3, "csh") ) + shell = getenv ("SHELL"); + if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; opt.homedir = getenv("GNUPGHOME"); @@ -275,6 +284,15 @@ main (int argc, char **argv ) opt.homedir = pargs.r.ret_str; } + /* initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + Now we are now working under our real uid + */ + + if (default_config) configname = make_filename (opt.homedir, "gpg-agent.conf", NULL ); @@ -648,6 +666,7 @@ start_listening (const char *name) return -1; } +#if 0 /* Look for the passprase as given by the 20 bytes DATA and return it's slot number. If this passphrase is not in the cache, return -1 */ static int @@ -751,7 +770,6 @@ passphrase_dialog ( const byte *fpr, const char *user_string ) } - static int writen ( int fd, const void *buf, size_t nbytes ) { @@ -993,6 +1011,6 @@ process_request ( int fd ) return; #endif } - +#endif diff --git a/agent/pksign.c b/agent/pksign.c index 69093a07c..f597c8aa2 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -24,16 +24,149 @@ #include #include #include +#include #include #include "agent.h" -/* SIGN whatever information we have accumulated in CTRL and write it back to - OUTFP. */ +static int +do_encode_md (const unsigned char *digest, size_t digestlen, int algo, + unsigned int nbits, GCRY_MPI *r_val) +{ + int nframe = (nbits+7) / 8; + byte *frame; + int i, n; + byte asn[100]; + size_t asnlen; + + asnlen = DIM(asn); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("No object identifier for algo %d\n", algo); + return GNUPG_Internal_Error; + } + + if (digestlen + asnlen + 4 > nframe ) + { + log_error ("can't encode a %d bit MD into a %d bits frame\n", + (int)(digestlen*8), (int)nbits); + return GNUPG_Internal_Error; + } + + /* We encode the MD in this way: + * + * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = xtrymalloc (nframe); + if (!frame) + return GNUPG_Out_Of_Core; + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - digestlen - asnlen -3 ; + assert ( i > 1 ); + memset ( frame+n, 0xff, i ); n += i; + frame[n++] = 0; + memcpy ( frame+n, asn, asnlen ); n += asnlen; + memcpy ( frame+n, digest, digestlen ); n += digestlen; + assert ( n == nframe ); + if (DBG_CRYPTO) + { + int j; + log_debug ("encoded hash:"); + for (j=0; j < nframe; j++) + log_printf (" %02X", frame[j]); + log_printf ("\n"); + } + + gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, &nframe); + xfree (frame); + return 0; +} + + +/* SIGN whatever information we have accumulated in CTRL and write it + back to OUTFP. */ int agent_pksign (CTRL ctrl, FILE *outfp) { + /* our sample key */ + const char n[] = "#8732A669BB7C5057AD070EFA54E035C86DF474F7A7EBE2435" + "3DADEB86FFE74C32AEEF9E5C6BD7584CB572520167B3E8C89A1FA75C74FF9E938" + "2710F3B270B638EB96E7486491D81C53CA8A50B4E840B1C7458A4A1E52EC18D681" + "8A2805C9165827F77EF90D55014E4B2AF9386AE8F6462F46A547CB593ABD509311" + "4D3D16375F#"; + const char e[] = "#11#"; + const char d[] = "#07F3EBABDDDA22D7FB1E8869140D30571586D9B4370DE02213F" + "DD0DDAC3C24FC6BEFF0950BB0CAAD755F7AA788DA12BCF90987341AC8781CC7115" + "B59A115B05D9D99B3D7AF77854DC2EE6A36154512CC0EAD832601038A88E837112" + "AB2A39FD9FBE05E30D6FFA6F43D71C59F423CA43BC91C254A8C89673AB61F326B0" + "762FBC9#"; + const char p[] = "#B2ABAD4328E66303E206C53CFBED17F18F712B1C47C966EE13DD" + "AA9AD3616A610ADF513F8376FA48BAE12FED64CECC1E73091A77B45119AF0FC1286A" + "85BD9BBD#"; + const char q[] = "#C1B648B294BB9AEE7FEEB77C4F64E9333E4EA9A7C54D521356FB" + "BBB7558A0E7D6331EC7B42E3F0CD7BBBA9B7A013422F615F10DCC1E8462828BF8FC7" + "39C5E34B#"; + const char u[] = "#A9B5EFF9C80A4A356B9A95EB63E381B262071E5CE9C1F32FF03" + "83AD8289BED8BC690555E54411FA2FDB9B49638A21B2046C325F5633B4B1ECABEBFD" + "1B3519072#"; - return -1; + GCRY_SEXP s_skey, s_hash, s_sig; + GCRY_MPI frame; + int rc; + char *buf; + size_t len; + + /* create a secret key as an sexp */ + log_debug ("Using HARDWIRED secret key\n"); + asprintf (&buf, "(private-key(oid.1.2.840.113549.1.1.1" + "(n %s)(e %s)(d %s)(p %s)(q %s)(u %s)))", + n, e, d, p, q, u); + /* asprintf does not use our allocation fucntions, so we can't + use our free */ + rc = gcry_sexp_sscan (&s_skey, NULL, buf, strlen(buf)); + free (buf); + if (rc) + { + log_error ("failed to build S-Exp: %s\n", gcry_strerror (rc)); + return map_gcry_err (rc); + } + + /* put the hash into a sexp */ + rc = do_encode_md (ctrl->digest.value, + ctrl->digest.valuelen, + ctrl->digest.algo, + gcry_pk_get_nbits (s_skey), + &frame); + if (rc) + { + /* fixme: clean up some things */ + return rc; + } + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + + + /* sign */ + rc = gcry_pk_sign (&s_sig, s_hash, s_skey); + if (rc) + { + log_error ("signing failed: %s\n", gcry_strerror (rc)); + return map_gcry_err (rc); + } + + 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); + + fwrite (buf, 1, strlen(buf), outfp); + return 0; } + +