Added decryption, some code cleanup
authorWerner Koch <wk@gnupg.org>
Wed, 5 Dec 2001 23:49:07 +0000 (23:49 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 5 Dec 2001 23:49:07 +0000 (23:49 +0000)
agent/Makefile.am
agent/agent.h
agent/command.c
agent/findkey.c [new file with mode: 0644]
agent/gpg-agent.c
agent/pkdecrypt.c [new file with mode: 0644]
agent/pksign.c

index a8172be..ce0b592 100644 (file)
@@ -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 \
index 95bfa08..e3ba2f2 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef AGENT_H
 #define AGENT_H
 
+#include <gcrypt.h>
 #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*/
index 80e1318..4e3da80 100644 (file)
 #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;
+}
+
+
+\f
 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 <hexstring_with_keygrip>
-
-   Set the  key used for a sign operation */
+   SETKEY <hexstring_with_keygrip>
+  
+   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 <options>
 
    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 <options>
+
+   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 (file)
index 0000000..9b0eb15
--- /dev/null
@@ -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 <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#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;
+}
+
index 71e9090..28f20da 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
+#include <stdarg.h>
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
@@ -33,6 +34,7 @@
 
 #include <gcrypt.h>
 
+#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 (file)
index 0000000..78f70ad
--- /dev/null
@@ -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 <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#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;
+}
+
+
index 4bf833e..9d1ad4f 100644 (file)
@@ -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;
 }