From 37be272ed854fcec8dd44c782258eaa341395caf Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 5 Dec 2001 23:49:07 +0000 Subject: [PATCH] Added decryption, some code cleanup --- agent/Makefile.am | 4 +- agent/agent.h | 9 ++++ agent/command.c | 84 +++++++++++++++++++++++++++++++--- agent/findkey.c | 91 ++++++++++++++++++++++++++++++++++++ agent/gpg-agent.c | 52 +++++++++++++++++++-- agent/pkdecrypt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++ agent/pksign.c | 114 ++++++++++++++-------------------------------- 7 files changed, 375 insertions(+), 91 deletions(-) create mode 100644 agent/findkey.c create mode 100644 agent/pkdecrypt.c diff --git a/agent/Makefile.am b/agent/Makefile.am index a8172bece..ce0b59218 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -26,7 +26,9 @@ LDFLAGS = @LDFLAGS@ gpg_agent_SOURCES = \ gpg-agent.c agent.h \ command.c \ - pksign.c + findkey.c \ + pksign.c \ + pkdecrypt.c gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ ../common/libcommon.a \ diff --git a/agent/agent.h b/agent/agent.h index 95bfa080e..e3ba2f262 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -21,6 +21,7 @@ #ifndef AGENT_H #define AGENT_H +#include #include "../common/util.h" #include "../common/errors.h" @@ -60,6 +61,7 @@ struct server_control_s { int valuelen; } digest; char keygrip[20]; + int have_keygrip; }; typedef struct server_control_s *CTRL; @@ -71,9 +73,16 @@ void agent_exit (int rc); /*-- command.c --*/ void start_command_handler (void); +/*-- findkey.c --*/ +GCRY_SEXP agent_key_from_file (const unsigned char *grip); + + /*-- pksign.c --*/ int agent_pksign (CTRL ctrl, FILE *outfp); +/*-- pkdecrypt.c --*/ +int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, + FILE *outfp); #endif /*AGENT_H*/ diff --git a/agent/command.c b/agent/command.c index 80e13182d..4e3da80a4 100644 --- a/agent/command.c +++ b/agent/command.c @@ -32,6 +32,10 @@ #include "agent.h" #include "../assuan/assuan.h" +/* maximum allowed size of the inquired ciphertext */ +#define MAXLEN_CIPHERTEXT 4096 + + #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) #define digitp(a) ((a) >= '0' && (a) <= '9') #define hexdigitp(a) (digitp (a) \ @@ -55,18 +59,62 @@ struct server_local_s { }; +/* Map GNUPG_xxx error codes to Assuan status codes + FIXME: duplicated from ../sm/server.c */ +static int +rc_to_assuan_status (int rc) +{ + switch (rc) + { + case 0: break; + case GNUPG_Bad_Certificate: rc = ASSUAN_Bad_Certificate; break; + case GNUPG_Bad_Certificate_Path: rc = ASSUAN_Bad_Certificate_Path; break; + case GNUPG_Missing_Certificate: rc = ASSUAN_Missing_Certificate; break; + case GNUPG_No_Data: rc = ASSUAN_No_Data_Available; break; + case GNUPG_Bad_Signature: rc = ASSUAN_Bad_Signature; break; + case GNUPG_Not_Implemented: rc = ASSUAN_Not_Implemented; break; + case GNUPG_No_Agent: rc = ASSUAN_No_Agent; break; + case GNUPG_Agent_Error: rc = ASSUAN_Agent_Error; break; + case GNUPG_No_Public_Key: rc = ASSUAN_No_Public_Key; break; + case GNUPG_No_Secret_Key: rc = ASSUAN_No_Secret_Key; break; + case GNUPG_Invalid_Data: rc = ASSUAN_Invalid_Data; break; + + case GNUPG_Read_Error: + case GNUPG_Write_Error: + case GNUPG_IO_Error: + rc = ASSUAN_Server_IO_Error; + break; + case GNUPG_Out_Of_Core: + case GNUPG_Resource_Limit: + rc = ASSUAN_Server_Resource_Problem; + break; + case GNUPG_Bug: + case GNUPG_Internal_Error: + rc = ASSUAN_Server_Bug; + break; + default: + rc = ASSUAN_Server_Fault; + break; + } + return rc; +} + + + static void reset_notify (ASSUAN_CONTEXT ctx) { CTRL ctrl = assuan_get_pointer (ctx); memset (ctrl->keygrip, 0, 20); + ctrl->have_keygrip = 0; ctrl->digest.valuelen = 0; } /* SIGKEY - - Set the key used for a sign operation */ + SETKEY + + Set the key used for a sign or decrypt operation */ static int cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) { @@ -89,6 +137,7 @@ cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) buf = ctrl->keygrip; for (p=line, n=0; n < 20; p += 2, n++) buf[n] = xtoi_2 (p); + ctrl->have_keygrip = 1; return 0; } @@ -140,17 +189,38 @@ cmd_sethash (ASSUAN_CONTEXT ctx, char *line) /* PKSIGN Perform the actual sign operation. Neither input nor output are - sensitive to to eavesdropping */ + sensitive 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; + return rc_to_assuan_status (rc); +} + +/* PKDECRYPT + + Perform the actual decrypt operation. Input is not + sensitive to eavesdropping */ +static int +cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + CTRL ctrl = assuan_get_pointer (ctx); + char *value; + size_t valuelen; + + /* First inquire the data to decrypt */ + rc = assuan_inquire (ctx, "CIPHERTEXT", + &value, &valuelen, MAXLEN_CIPHERTEXT); + if (rc) + return rc; + + rc = agent_pkdecrypt (ctrl, value, valuelen, assuan_get_data_fp (ctx)); + xfree (value); + return rc_to_assuan_status (rc); } @@ -165,8 +235,10 @@ register_commands (ASSUAN_CONTEXT ctx) int (*handler)(ASSUAN_CONTEXT, char *line); } table[] = { { "SIGKEY", 0, cmd_sigkey }, + { "SETKEY", 0, cmd_sigkey }, { "SETHASH", 0, cmd_sethash }, { "PKSIGN", 0, cmd_pksign }, + { "PKDECRYPT", 0, cmd_pkdecrypt }, { "", ASSUAN_CMD_INPUT, NULL }, { "", ASSUAN_CMD_OUTPUT, NULL }, { NULL } diff --git a/agent/findkey.c b/agent/findkey.c new file mode 100644 index 000000000..9b0eb157b --- /dev/null +++ b/agent/findkey.c @@ -0,0 +1,91 @@ +/* findkey.c - locate the 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 +#include + +#include "agent.h" + +/* Return the secret key as an S-Exp after locating it using the grip. Returns NULL if key is not available. */ +GCRY_SEXP +agent_key_from_file (const unsigned char *grip) +{ + int i, rc; + char *fname; + FILE *fp; + struct stat st; + char *buf; + size_t buflen, erroff; + GCRY_SEXP s_skey; + char hexgrip[41]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + hexgrip[40] = 0; + + fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL ); + fp = fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat `%s': %s\n", fname, strerror (errno)); + xfree (fname); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading `%s': %s\n", fname, strerror (errno)); + xfree (fname); + fclose (fp); + xfree (buf); + return NULL; + } + + rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen); + xfree (fname); + fclose (fp); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gcry_strerror (rc)); + return NULL; + } + + return s_skey; +} + diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 71e909057..28f20dab0 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include +#define JNLIB_NEED_LOG_LOGV #include "agent.h" #include "../assuan/assuan.h" /* malloc hooks */ @@ -177,6 +179,27 @@ cleanup (void) } } + +/* Use by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + static RETSIGTYPE cleanup_sh (int sig) { @@ -224,13 +247,12 @@ main (int argc, char **argv ) int csh_style = 0; char *logfile = NULL; - set_strusage( my_strusage ); + 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() */ + log_set_prefix ("gpg-agent", 1|4); i18n_init (); /* check that the libraries are suitable. Do it here because @@ -242,6 +264,7 @@ main (int argc, char **argv ) } assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = 0/* FIXME: disable_core_dumps()*/; @@ -452,6 +475,19 @@ main (int argc, char **argv ) } else if (server_mode) { /* for now this is the simple pipe based server */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); + } + + if ( atexit( cleanup ) ) + { + log_error ("atexit failed\n"); + cleanup (); + exit (1); + } + start_command_handler (); } else @@ -518,11 +554,17 @@ main (int argc, char **argv ) } /* end parent */ if ( (opt.debug & 1) ) - sleep( 20 ); /* give us some time to attach gdb to the child */ + { + fprintf (stderr, "... 20 seconds to attach the debugger ..."); + fflush (stderr); + sleep( 20 ); /* give us some time to attach gdb to the child */ + putc ('\n', stderr); + } if (logfile) { - /* FIXME:log_set_logfile (opt.logfile, -1);*/ + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); } if ( atexit( cleanup ) ) diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c new file mode 100644 index 000000000..78f70ad52 --- /dev/null +++ b/agent/pkdecrypt.c @@ -0,0 +1,112 @@ +/* pkdecrypt.c - public key decryption (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 +#include + +#include "agent.h" + + +/* DECRYPT the stuff in ciphertext which is expected to be a S-Exp. + Try to get the key from CTRL and write the decoded stuff back to + OUTFP. */ +int +agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, + FILE *outfp) +{ + GCRY_SEXP s_skey = NULL, s_cipher = NULL, s_plain = NULL; + int rc; + char *buf = NULL; + size_t len; + + if (!ctrl->have_keygrip) + { + log_error ("speculative decryption not yet supported\n"); + rc = seterr (No_Secret_Key); + goto leave; + } + + rc = gcry_sexp_sscan (&s_cipher, NULL, ciphertext, ciphertextlen); + if (rc) + { + log_error ("failed to convert ciphertext: %s\n", gcry_strerror (rc)); + rc = seterr (Invalid_Data); + goto leave; + } + + if (DBG_CRYPTO) + { + log_printhex ("keygrip:", ctrl->keygrip, 20); + log_printhex ("cipher: ", ciphertext, ciphertextlen); + } + s_skey = agent_key_from_file (ctrl->keygrip); + if (!s_skey) + { + log_error ("failed to read the secret key\n"); + rc = seterr (No_Secret_Key); + goto leave; + } + + if (DBG_CRYPTO) + { + log_debug ("skey: "); + gcry_sexp_dump (s_skey); + } + + rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey); + if (rc) + { + log_error ("decryption failed: %s\n", gcry_strerror (rc)); + rc = map_gcry_err (rc); + goto leave; + } + + if (DBG_CRYPTO) + { + log_debug ("plain: "); + gcry_sexp_dump (s_plain); + } + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + + /* FIXME: we must make sure that no buffering takes place or we are + in full control of the buffer memory (easy to do) - should go + into assuan. */ + fwrite (buf, 1, len, outfp); + + leave: + gcry_sexp_release (s_skey); + gcry_sexp_release (s_plain); + gcry_sexp_release (s_cipher); + xfree (buf); + return rc; +} + + diff --git a/agent/pksign.c b/agent/pksign.c index 4bf833ece..9d1ad4f67 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -75,13 +75,7 @@ do_encode_md (const unsigned char *digest, size_t digestlen, int algo, 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"); - } + log_printhex ("encoded hash:", frame, nframe); gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, &nframe); xfree (frame); @@ -89,82 +83,26 @@ do_encode_md (const unsigned char *digest, size_t digestlen, int algo, } -static GCRY_SEXP -key_from_file (const unsigned char *grip) -{ - int i, rc; - char *fname; - FILE *fp; - struct stat st; - char *buf; - size_t buflen, erroff; - GCRY_SEXP s_skey; - char hexgrip[41]; - - for (i=0; i < 20; i++) - sprintf (hexgrip+2*i, "%02X", grip[i]); - hexgrip[40] = 0; - - fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL ); - fp = fopen (fname, "rb"); - if (!fp) - { - log_error ("can't open `%s': %s\n", fname, strerror (errno)); - xfree (fname); - return NULL; - } - - if (fstat (fileno(fp), &st)) - { - log_error ("can't stat `%s': %s\n", fname, strerror (errno)); - xfree (fname); - fclose (fp); - return NULL; - } - - buflen = st.st_size; - buf = xmalloc (buflen+1); - if (fread (buf, buflen, 1, fp) != 1) - { - log_error ("error reading `%s': %s\n", fname, strerror (errno)); - xfree (fname); - fclose (fp); - xfree (buf); - return NULL; - } - - rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen); - xfree (fname); - fclose (fp); - xfree (buf); - if (rc) - { - log_error ("failed to build S-Exp (off=%u): %s\n", - (unsigned int)erroff, gcry_strerror (rc)); - return NULL; - } - - return s_skey; -} - - - /* SIGN whatever information we have accumulated in CTRL and write it back to OUTFP. */ int agent_pksign (CTRL ctrl, FILE *outfp) { - GCRY_SEXP s_skey, s_hash, s_sig; - GCRY_MPI frame; + GCRY_SEXP s_skey = NULL, s_hash = NULL, s_sig = NULL; + GCRY_MPI frame = NULL; int rc; - char *buf; + char *buf = NULL; size_t len; - s_skey = key_from_file (ctrl->keygrip); + if (!ctrl->have_keygrip) + return seterr (No_Secret_Key); + + s_skey = agent_key_from_file (ctrl->keygrip); if (!s_skey) { log_error ("failed to read the secret key\n"); - return seterr (No_Secret_Key); + rc = seterr (No_Secret_Key); + goto leave; } /* put the hash into a sexp */ @@ -174,22 +112,33 @@ agent_pksign (CTRL ctrl, FILE *outfp) gcry_pk_get_nbits (s_skey), &frame); if (rc) - { - /* fixme: clean up some things */ - return rc; - } + goto leave; if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) BUG (); + if (DBG_CRYPTO) + { + log_debug ("skey: "); + gcry_sexp_dump (s_skey); + } + /* 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); + rc = map_gcry_err (rc); + goto leave; } + if (DBG_CRYPTO) + { + log_debug ("result: "); + gcry_sexp_dump (s_sig); + } + + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0); assert (len); buf = xmalloc (len); @@ -199,8 +148,15 @@ agent_pksign (CTRL ctrl, FILE *outfp) /* FIXME: we must make sure that no buffering takes place or we are in full control of the buffer memory (easy to do) - should go into assuan. */ - fwrite (buf, 1, strlen(buf), outfp); - return 0; + fwrite (buf, 1, len, outfp); + + leave: + gcry_sexp_release (s_skey); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_sig); + gcry_mpi_release (frame); + xfree (buf); + return rc; }