* Makefile.am (pkglib_PROGRAMS): Put protect-tool there.
authorWerner Koch <wk@gnupg.org>
Thu, 27 Jun 2002 07:29:29 +0000 (07:29 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 27 Jun 2002 07:29:29 +0000 (07:29 +0000)
* findkey.c (agent_write_private_key,agent_key_from_file)
(agent_key_available): Use GNUPG_PRIVATE_KEYS_DIR constant.
* gpg-agent.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.

* protect-tool.c (store_private_key): New.
(import_p12_file): Store the new file if requested.
(main): New options --force and --store.

* gpg-agent.c (main): Set a global flag when runing detached.
* query.c (start_pinentry): Pass the list of FD to keep in the
child when not running detached.
* call-scd.c (start_scd): Ditto.

13 files changed:
agent/ChangeLog
agent/Makefile.am
agent/agent.h
agent/call-scd.c
agent/command.c
agent/divert-scd.c
agent/findkey.c
agent/gpg-agent.c
agent/minip12.c
agent/protect-tool.c
agent/query.c
agent/simple-pwquery.c [new file with mode: 0644]
agent/simple-pwquery.h [new file with mode: 0644]

index 3a37ec8..cd1f04f 100644 (file)
@@ -1,3 +1,30 @@
+2002-06-27  Werner Koch  <wk@gnupg.org>
+
+       * Makefile.am (pkglib_PROGRAMS): Put protect-tool there.
+
+       * findkey.c (agent_write_private_key,agent_key_from_file)
+       (agent_key_available): Use GNUPG_PRIVATE_KEYS_DIR constant.
+       * gpg-agent.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
+
+       * protect-tool.c (store_private_key): New.
+       (import_p12_file): Store the new file if requested.
+       (main): New options --force and --store.
+
+       * gpg-agent.c (main): Set a global flag when runing detached.
+       * query.c (start_pinentry): Pass the list of FD to keep in the
+       child when not running detached.
+       * call-scd.c (start_scd): Ditto.
+
+2002-06-26  Werner Koch  <wk@gnupg.org>
+
+       * command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted)
+       (cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_get_passphrase)
+       (cmd_learn): Print an error message for a failed operation.
+
+       * simple-pwquery.c, simple-pwquery.h: New.
+       * protect-tool. (get_passphrase): New, used to ge a passphrase
+       from the agent if none was given on the command line.
+
 2002-06-25  Werner Koch  <wk@gnupg.org>
 
        * protect-tool.c (rsa_key_check): New.
index baeab5d..c814dd9 100644 (file)
@@ -19,7 +19,7 @@
 ## Process this file with automake to produce Makefile.in
 
 bin_PROGRAMS = gpg-agent
-noinst_PROGRAMS = protect-tool
+pkglib_PROGRAMS = protect-tool
 
 AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) $(PTH_CFLAGS)
 LDFLAGS = @LDFLAGS@ 
@@ -48,8 +48,8 @@ gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a  \
 protect_tool_SOURCES = \
        protect-tool.c \
        protect.c \
-       minip12.c minip12.h
-
+       minip12.c minip12.h \
+       simple-pwquery.c simple-pwquery.h
 
 protect_tool_LDADD = ../jnlib/libjnlib.a \
                     ../common/libcommon.a $(LIBGCRYPT_LIBS)
index 75683a0..ba69343 100644 (file)
@@ -45,6 +45,8 @@ struct {
   int no_grab;      /* don't let the pinentry grab the keyboard */
   unsigned long def_cache_ttl;
 
+  int running_detached; /* we are running detached from the tty. */
+
 } opt;
 
 
index 8b79e81..5f7de05 100644 (file)
@@ -156,6 +156,8 @@ start_scd (void)
   const char *pgmname;
   ASSUAN_CONTEXT ctx;
   const char *argv[3];
+  int no_close_list[3];
+  int i;
 
 #ifdef USE_GNU_PTH
   if (!pth_mutex_acquire (&scd_lock, 0, NULL))
@@ -191,8 +193,18 @@ start_scd (void)
   argv[1] = "--server";
   argv[2] = NULL;
 
+  i=0;
+  if (!opt.running_detached)
+    {
+      if (log_get_fd () != -1)
+        no_close_list[i++] = log_get_fd ();
+      no_close_list[i++] = fileno (stderr);
+    }
+  no_close_list[i] = -1;
+
   /* connect to the pinentry and perform initial handshaking */
-  rc = assuan_pipe_connect (&ctx, opt.scdaemon_program, (char**)argv, 0);
+  rc = assuan_pipe_connect (&ctx, opt.scdaemon_program, (char**)argv,
+                            no_close_list);
   if (rc)
     {
       log_error ("can't connect to the SCdaemon: %s\n",
index 387eef4..b98ec25 100644 (file)
@@ -110,7 +110,10 @@ cmd_istrusted (ASSUAN_CONTEXT ctx, char *line)
   else if (rc == -1)
     return ASSUAN_Not_Trusted;
   else
-    return map_to_assuan_status (rc);
+    {
+      log_error ("command is_trusted failed: %s\n", gnupg_strerror (rc));
+      return map_to_assuan_status (rc);
+    }
 }
 
 /* LISTTRUSTED 
@@ -119,7 +122,10 @@ cmd_istrusted (ASSUAN_CONTEXT ctx, char *line)
 static int
 cmd_listtrusted (ASSUAN_CONTEXT ctx, char *line)
 {
-  return map_to_assuan_status (agent_listtrusted (ctx));
+  int rc = agent_listtrusted (ctx);
+  if (rc)
+    log_error ("command listtrusted failed: %s\n", gnupg_strerror (rc));
+  return map_to_assuan_status (rc);
 }
 
 
@@ -158,6 +164,8 @@ cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line)
     p++;
 
   rc = agent_marktrusted (p, fpr, flag);
+  if (rc)
+    log_error ("command marktrusted failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
@@ -281,6 +289,8 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
   CTRL ctrl = assuan_get_pointer (ctx);
 
   rc = agent_pksign (ctrl, assuan_get_data_fp (ctx));
+  if (rc)
+    log_error ("command pksign failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
@@ -304,6 +314,8 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
 
   rc = agent_pkdecrypt (ctrl, value, valuelen, assuan_get_data_fp (ctx));
   xfree (value);
+  if (rc)
+    log_error ("command pkdecrypt failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
@@ -337,6 +349,8 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
 
   rc = agent_genkey (ctrl, value, valuelen, assuan_get_data_fp (ctx));
   xfree (value);
+  if (rc)
+    log_error ("command genkey failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
@@ -449,6 +463,8 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line)
         }
     }
 
+  if (rc)
+    log_error ("command get_passphrase failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
@@ -491,7 +507,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
 
   rc = agent_handle_learn (has_option (line, "--send")? ctx : NULL);
   if (rc)
-    log_error ("agent_handle_learn failed: %s\n", gnupg_strerror (rc));
+    log_error ("command learn failed: %s\n", gnupg_strerror (rc));
   return map_to_assuan_status (rc);
 }
 
index f2a26ac..dfc1008 100644 (file)
@@ -173,7 +173,6 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
 {
   struct pin_entry_info_s *pi;
   int rc;
-  int tries = 0;
 
   assert (!opaque);
 
index 3a9dd53..fb3b36e 100644 (file)
@@ -52,7 +52,7 @@ agent_write_private_key (const unsigned char *grip,
     sprintf (hexgrip+2*i, "%02X", grip[i]);
   strcpy (hexgrip+40, ".key");
 
-  fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL);
+  fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
   if (force)
     fp = fopen (fname, "wb");
   else
@@ -194,7 +194,7 @@ agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info)
     sprintf (hexgrip+2*i, "%02X", grip[i]);
   strcpy (hexgrip+40, ".key");
 
-  fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL);
+  fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
   fp = fopen (fname, "rb");
   if (!fp)
     {
@@ -318,7 +318,7 @@ agent_key_available (const unsigned char *grip)
     sprintf (hexgrip+2*i, "%02X", grip[i]);
   strcpy (hexgrip+40, ".key");
 
-  fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL);
+  fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
   i = !access (fname, R_OK)? 0 : -1;
   xfree (fname);
   return i;
index 7742e1b..4441cf5 100644 (file)
@@ -295,13 +295,7 @@ main (int argc, char **argv )
   
   opt.homedir = getenv("GNUPGHOME");
   if (!opt.homedir || !*opt.homedir)
-    {
-#ifdef HAVE_DRIVE_LETTERS
-      opt.homedir = "c:/gnupg-test";
-#else
-      opt.homedir = "~/.gnupg-test";
-#endif
-    }
+    opt.homedir = GNUPG_DEFAULT_HOMEDIR;
   opt.def_cache_ttl = 10*60; /* default to 10 minutes */
 
 
@@ -473,7 +467,6 @@ main (int argc, char **argv )
     { /* regular server mode */
       int fd;
       pid_t pid;
-      int i;
       int len;
       struct sockaddr_un serv_addr;
       char *p;
@@ -599,7 +592,10 @@ main (int argc, char **argv )
 
       /* detach from tty and put process into a new session */
       if (!nodetach )
-        {  /* close stdin, stdout and stderr unless it is the log stream */
+        { 
+          int i;
+
+          /* close stdin, stdout and stderr unless it is the log stream */
           for (i=0; i <= 2; i++) 
             {
               if ( log_get_fd () != i)
@@ -611,6 +607,7 @@ main (int argc, char **argv )
               cleanup ();
               exit (1);
             }
+          opt.running_detached = 1;
         }
 
       if (chdir("/"))
index 29531b2..6fe9d2c 100644 (file)
@@ -660,7 +660,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw)
   bagseqlength = ti.length;
   while (bagseqlength)
     {
-      log_error ( "at offset %u\n", (p - buffer));
+      /*log_debug ( "at offset %u\n", (p - buffer));*/
       where = "bag-sequence";
       if (parse_tag (&p, &n, &ti))
         goto bailout;
index f533425..4efe497 100644 (file)
@@ -35,6 +35,7 @@
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
 #include "minip12.h"
+#include "simple-pwquery.h"
 
 #define N_(a) a
 #define _(a) a
@@ -55,6 +56,8 @@ enum cmd_and_opt_values
   oShowKeygrip,
 
   oP12Import,
+  oStore,
+  oForce,
 
 aTest };
 
@@ -70,7 +73,14 @@ struct rsa_secret_key_s
 
 
 static int opt_armor;
-static const char *passphrase = "abc";
+static int opt_store;
+static int opt_force;
+static const char *passphrase;
+
+static const char *get_passphrase (void);
+static int store_private_key (const unsigned char *grip,
+                              const void *buffer, size_t length, int force);
+
 
 static ARGPARSE_OPTS opts[] = {
   
@@ -86,6 +96,8 @@ static ARGPARSE_OPTS opts[] = {
   { oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""},
 
   { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
+  { oStore,     "store", 0, "store the created key in the appropriate place"},
+  { oForce,     "force", 0, "force overwriting"},
   {0}
 };
 
@@ -283,7 +295,7 @@ read_and_protect (const char *fname)
   if (!key)
     return;
 
-  rc = agent_protect (key, passphrase, &result, &resultlen);
+  rc = agent_protect (key, get_passphrase (), &result, &resultlen);
   xfree (key);
   if (rc)
     {
@@ -318,7 +330,7 @@ read_and_unprotect (const char *fname)
   if (!key)
     return;
 
-  rc = agent_unprotect (key, passphrase, &result, &resultlen);
+  rc = agent_unprotect (key, get_passphrase (), &result, &resultlen);
   xfree (key);
   if (rc)
     {
@@ -564,6 +576,7 @@ import_p12_file (const char *fname)
   struct rsa_secret_key_s sk;
   GcrySexp s_key;
   unsigned char *key;
+  unsigned char grip[20];
 
   /* fixme: we should release some stuff on error */
   
@@ -571,7 +584,7 @@ import_p12_file (const char *fname)
   if (!buf)
     return;
 
-  kparms = p12_parse (buf, buflen, passphrase);
+  kparms = p12_parse (buf, buflen, get_passphrase ());
   xfree (buf);
   if (!kparms)
     {
@@ -626,18 +639,15 @@ import_p12_file (const char *fname)
     }
 
   /* Compute the keygrip. */
-  {
-    unsigned char grip[20];
-    if (!gcry_pk_get_keygrip (s_key, grip))
-      {
-        log_error ("can't calculate keygrip\n");
-        return;
-      }
-    log_info ("keygrip: ");
-    for (i=0; i < 20; i++)
-      log_printf ("%02X", grip[i]);
-    log_printf ("\n");
-  }
+  if (!gcry_pk_get_keygrip (s_key, grip))
+    {
+      log_error ("can't calculate keygrip\n");
+      return;
+    }
+  log_info ("keygrip: ");
+  for (i=0; i < 20; i++)
+    log_printf ("%02X", grip[i]);
+  log_printf ("\n");
 
   /* convert to canonical encoding */
   buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, NULL, 0);
@@ -648,7 +658,7 @@ import_p12_file (const char *fname)
   gcry_sexp_release (s_key);
 
 
-  rc = agent_protect (key, passphrase, &result, &resultlen);
+  rc = agent_protect (key, get_passphrase (), &result, &resultlen);
   xfree (key);
   if (rc)
     {
@@ -666,7 +676,11 @@ import_p12_file (const char *fname)
       resultlen = strlen (p);
     }
 
-  fwrite (result, resultlen, 1, stdout);
+  if (opt_store)
+    store_private_key (grip, result, resultlen, opt_force);
+  else
+    fwrite (result, resultlen, 1, stdout);
+
   xfree (result);
 }
 
@@ -711,6 +725,8 @@ main (int argc, char **argv )
         case oP12Import: cmd = oP12Import; break;
 
         case oPassphrase: passphrase = pargs.r.ret_str; break;
+        case oStore: opt_store = 1; break;
+        case oForce: opt_force = 1; break;
 
         default : pargs.err = 2; break;
        }
@@ -736,7 +752,8 @@ main (int argc, char **argv )
   else
     show_file (*argv);
 
-  return 0;
+  agent_exit (0);
+  return 8; /*NOTREACHED*/
 }
 
 void
@@ -745,3 +762,95 @@ agent_exit (int rc)
   rc = rc? rc : log_get_errorcount(0)? 2 : 0;
   exit (rc);
 }
+
+
+/* Return the passphrase string and ask the agent if it has not been
+   set from the command line. */
+static const char *
+get_passphrase (void)
+{
+  char *pw;
+  int err;
+
+  if (passphrase)
+    return passphrase;
+
+  pw = simple_pwquery (NULL,NULL, 
+                       _("Enter passphrase:"),
+                       _("Please enter the passphrase or the PIN\n"
+                         "needed to complete this operation."),
+                       &err);
+  if (!pw)
+    {
+      if (err)
+        log_error ("error while asking for the passphrase\n");
+      else
+        log_info ("cancelled\n");
+      agent_exit (0);
+    }
+  passphrase = pw;
+  return passphrase;
+}
+
+
+static int
+store_private_key (const unsigned char *grip,
+                   const void *buffer, size_t length, int force)
+{
+  int i;
+  const char *homedir;
+  char *fname;
+  FILE *fp;
+  char hexgrip[40+4+1];
+  
+  for (i=0; i < 20; i++)
+    sprintf (hexgrip+2*i, "%02X", grip[i]);
+  strcpy (hexgrip+40, ".key");
+
+  homedir = getenv("GNUPGHOME");
+  if (!homedir || !*homedir)
+    homedir = GNUPG_DEFAULT_HOMEDIR;
+
+  fname = make_filename (homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
+  if (force)
+    fp = fopen (fname, "wb");
+  else
+    {
+      if (!access (fname, F_OK))
+      {
+        log_error ("secret key file `%s' already exists\n", fname);
+        xfree (fname);
+        return -1;
+      }
+      fp = fopen (fname, "wbx");  /* FIXME: the x is a GNU extension - let
+                                     configure check whether this actually
+                                     works */
+    }
+
+  if (!fp) 
+    { 
+      log_error ("can't create `%s': %s\n", fname, strerror (errno));
+      xfree (fname);
+      return -1;
+    }
+
+  if (fwrite (buffer, length, 1, fp) != 1)
+    {
+      log_error ("error writing `%s': %s\n", fname, strerror (errno));
+      fclose (fp);
+      remove (fname);
+      xfree (fname);
+      return -1;
+    }
+  if ( fclose (fp) )
+    {
+      log_error ("error closing `%s': %s\n", fname, strerror (errno));
+      remove (fname);
+      xfree (fname);
+      return -1;
+    }
+  log_info ("secret key stored as `%s'\n", fname);
+
+  xfree (fname);
+  return 0;
+}
index 724bbd5..c7cba5e 100644 (file)
@@ -80,6 +80,8 @@ start_pinentry (void)
   const char *pgmname;
   ASSUAN_CONTEXT ctx;
   const char *argv[5];
+  int no_close_list[3];
+  int i;
 
 #ifdef USE_GNU_PTH
   if (!pth_mutex_acquire (&entry_lock, 0, NULL))
@@ -119,9 +121,19 @@ start_pinentry (void)
     }
   else
     argv[1] = NULL;
+  
+  i=0;
+  if (!opt.running_detached)
+    {
+      if (log_get_fd () != -1)
+        no_close_list[i++] = log_get_fd ();
+      no_close_list[i++] = fileno (stderr);
+    }
+  no_close_list[i] = -1;
 
   /* connect to the pinentry and perform initial handshaking */
-  rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv, 0);
+  rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv,
+                            no_close_list);
   if (rc)
     {
       log_error ("can't connect to the PIN entry module: %s\n",
diff --git a/agent/simple-pwquery.c b/agent/simple-pwquery.c
new file mode 100644 (file)
index 0000000..5bb08af
--- /dev/null
@@ -0,0 +1,485 @@
+/* simple-pwquery.c - A simple password query cleint for gpg-agent
+ *     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
+ */
+
+/* This module is intended as a standalone client implementation to
+   gpg-agent's GET_PASSPHRASE command.  In particular it does not use
+   the Assuan library and can only cope with an already running
+   gpg-agent.  Some stuff is configurable in the header file. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#define SIMPLE_PWQUERY_IMPLEMENTATION 1
+#include "simple-pwquery.h"
+
+#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
+# undef SPWQ_USE_LOGGING
+#endif
+
+#ifndef _
+#define _(a) (a)
+#endif
+
+#if !defined (hexdigitp) && !defined (xtoi_2)
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#endif
+
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+  size_t nleft = nbytes;
+  int nwritten;
+  
+  while (nleft > 0)
+    {
+      nwritten = write( fd, buf, nleft );
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            nwritten = 0;
+          else {
+#ifdef SPWQ_USE_LOGGING
+            log_error ("write failed: %s\n", strerror (errno));
+#endif
+            return SPWQ_IO_ERROR;
+          }
+        }
+      nleft -= nwritten;
+      buf = (const char*)buf + nwritten;
+    }
+    
+  return 0;
+}
+
+
+/* Read an entire line and return number of bytes read. */
+static int
+readline (int fd, char *buf, size_t buflen)
+{
+  size_t nleft = buflen;
+  char *p;
+  int nread = 0;
+
+  while (nleft > 0)
+    {
+      int n = read (fd, buf, nleft);
+      if (n < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -(SPWQ_IO_ERROR);
+        }
+      else if (!n)
+        {
+          return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
+        }
+      p = buf;
+      nleft -= n;
+      buf += n;
+      nread += n;
+      
+      for (; n && *p != '\n'; n--, p++)
+        ;
+      if (n)
+        {
+          break; /* at least one full line available - that's enough.
+                    This function is just a simple implementation, so
+                    it is okay to forget about pending bytes */
+        }
+    }
+
+  return nread; 
+}
+
+
+/* Send an option to the agent */
+static int
+agent_send_option (int fd, const char *name, const char *value)
+{
+  char buf[200];
+  int nread;
+  char *line;
+  int i; 
+  
+  line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
+  if (!line)
+    return SPWQ_OUT_OF_CORE;
+  strcpy (stpcpy (stpcpy (stpcpy (
+                     stpcpy (line, "OPTION "), name), "="), value), "\n");
+  i = writen (fd, line, strlen (line));
+  spwq_free (line);
+  if (i)
+    return i;
+  
+  /* get response */
+  nread = readline (fd, buf, DIM(buf)-1);
+  if (nread < 0)
+    return -nread;
+  if (nread < 3)
+    return SPWQ_PROTOCOL_ERROR;
+  
+  if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) 
+    return 0; /* okay */
+
+  return SPWQ_ERR_RESPONSE;
+}
+
+
+/* Send all available options to the agent. */
+static int 
+agent_send_all_options (int fd)
+{
+  char *dft_display = NULL;
+  char *dft_ttyname = NULL;
+  char *dft_ttytype = NULL;
+  int rc = 0;
+
+  dft_display = getenv ("DISPLAY");
+  if (dft_display)
+    {
+      if ((rc = agent_send_option (fd, "display", dft_display)))
+        return rc;
+    }
+
+  if (ttyname (1))
+    dft_ttyname = ttyname (1);
+  if (dft_ttyname)
+    {
+      if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
+        return rc;
+    }
+
+  dft_ttytype = getenv ("TERM");
+  if (dft_ttyname && dft_ttytype)
+    {
+      if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
+        return rc;
+    }
+
+#if defined(HAVE_SETLOCALE) 
+  {
+    char *old_lc = NULL;
+    char *dft_lc = NULL;
+
+#if defined(LC_CTYPE)
+    old_lc = setlocale (LC_CTYPE, NULL);
+    if (old_lc)
+      {
+        char *p = spwq_malloc (strlen (old_lc)+1);
+        if (!p)
+          return SPWQ_OUT_OF_CORE;
+        strcpy (p, old_lc);
+        old_lc = p;
+      }
+    dft_lc = setlocale (LC_CTYPE, "");
+    if (dft_ttyname && dft_lc)
+      rc = agent_send_option (fd, "lc-ctype", dft_lc);
+    if (old_lc)
+      {
+        setlocale (LC_CTYPE, old_lc);
+        spwq_free (old_lc);
+      }
+    if (rc)
+      return rc;
+#endif
+
+#if defined(LC_MESSAGES)
+    old_lc = setlocale (LC_MESSAGES, NULL);
+    if (old_lc)
+      {
+        char *p = spwq_malloc (strlen (old_lc)+1);
+        if (!p)
+          return SPWQ_OUT_OF_CORE;
+        strcpy (p, old_lc);
+        old_lc = p;
+      }
+    dft_lc = setlocale (LC_MESSAGES, "");
+    if (dft_ttyname && dft_lc)
+      rc = agent_send_option (fd, "lc-messages", dft_lc);
+    if (old_lc)
+      {
+        setlocale (LC_MESSAGES, old_lc);
+        spwq_free (old_lc);
+      }
+    if (rc)
+      return rc;
+#endif
+  }
+#endif /*HAVE_SETLOCALE*/
+
+  return 0;
+}
+
+
+
+/* Try to open a connection to the agent, send all options and return
+   the file descriptor for the connection.  Return -1 in case of
+   error. */
+static int
+agent_open (int *rfd)
+{
+  int rc;
+  int fd;
+  char *infostr, *p;
+  struct sockaddr_un client_addr;
+  size_t len;
+  int prot;
+  char line[200];
+  int nread;
+
+  *rfd = -1;
+  infostr = getenv ( "GPG_AGENT_INFO" );
+  if ( !infostr ) 
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error (_("gpg-agent is not available in this session\n"));
+#endif
+      return SPWQ_NO_AGENT;
+    }
+
+  if ( !(p = strchr ( infostr, ':')) || p == infostr
+       || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
+#endif
+      return SPWQ_NO_AGENT;
+    }
+  *p++ = 0;
+
+  while (*p && *p != ':')
+    p++;
+  prot = *p? atoi (p+1) : 0;
+  if ( prot != 1)
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
+#endif
+      return SPWQ_PROTOCOL_ERROR;
+    }
+       
+  if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) 
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error ("can't create socket: %s\n", strerror(errno) );
+#endif
+      return SPWQ_SYS_ERROR;
+    }
+    
+  memset (&client_addr, 0, sizeof client_addr);
+  client_addr.sun_family = AF_UNIX;
+  strcpy (client_addr.sun_path, infostr);
+  len = (offsetof (struct sockaddr_un, sun_path)
+         + strlen(client_addr.sun_path) + 1);
+    
+  if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1)
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
+#endif
+      close (fd );
+      return SPWQ_IO_ERROR;
+    }
+
+  nread = readline (fd, line, DIM(line));
+  if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
+                     && (line[2] == '\n' || line[2] == ' ')) ) 
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error ( _("communication problem with gpg-agent\n"));
+#endif
+      close (fd );
+      return SPWQ_PROTOCOL_ERROR;
+    }
+
+  rc = agent_send_all_options (fd);
+  if (rc)
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error (_("problem setting the gpg-agent options\n"));
+#endif
+      close (fd);
+      return rc;
+    }
+
+  *rfd = fd;
+  return 0;
+}
+
+
+/* Copy text to BUFFER and escape as required.  Return a poiinter to
+   the end of the new buffer.  NOte that BUFFER must be large enough
+   to keep the entire text; allocataing it 3 times the size of TEXT
+   is sufficient. */
+static char *
+copy_and_escape (char *buffer, const char *text)
+{
+  int i;
+  char *p = buffer;
+
+  for (i=0; text[i]; i++)
+    {
+      if (text[i] < ' ' || text[i] == '+')
+        {
+          sprintf (p, "%%%02X", text[i]);
+          p += 3;
+        }
+      else if (text[i] == ' ')
+        *p++ = '+';
+      else
+        *p++ = text[i];
+    }
+  return p;
+}
+
+
+/* Ask the gpg-agent for a passphrase and present the user with a
+   DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
+   If a CACHEID is not NULL it is used to locate the passphrase in in
+   the cache and store it under this ID.  If ERRORCODE is not NULL it
+   should point a variable receiving an errorcode; thsi errocode might
+   be 0 if the user canceled the operation.  The function returns NULL
+   to indicate an error. */
+char *
+simple_pwquery (const char *cacheid, 
+                const char *tryagain,
+                const char *prompt,
+                const char *description,
+                int *errorcode)
+{
+  int fd = -1;
+  int nread;
+  char *result = NULL;
+  char *pw = NULL;
+  char *p;
+  int rc, i; 
+
+  rc = agent_open (&fd);
+  if (rc)
+    goto leave;
+
+  if (!cacheid)
+    cacheid = "X";
+  if (!tryagain)
+    tryagain = "X";
+  if (!prompt)
+    prompt = "X";
+  if (!description)
+    description = "X";
+
+  {
+    char *line;
+    /* We allocate 3 times the needed space so that there is enough
+       space for escaping. */
+    line = spwq_malloc (15
+                        + 3*strlen (cacheid) + 1
+                        + 3*strlen (tryagain) + 1
+                        + 3*strlen (prompt) + 1
+                        + 3*strlen (description) + 1
+                        + 2);
+    if (!line)
+      {
+        rc = SPWQ_OUT_OF_CORE;
+        goto leave;
+      }
+    strcpy (line, "GET_PASSPHRASE ");
+    p = line+15;
+    p = copy_and_escape (p, cacheid);
+    *p++ = ' ';
+    p = copy_and_escape (p, tryagain);
+    *p++ = ' ';
+    p = copy_and_escape (p, prompt);
+    *p++ = ' ';
+    p = copy_and_escape (p, description);
+    *p++ = '\n';
+    rc = writen (fd, line, p - line);
+    spwq_free (line);
+    if (rc)
+      goto leave;
+  }
+
+  /* get response */
+  pw = spwq_secure_malloc (500);
+  nread = readline (fd, pw, 499);
+  if (nread < 0)
+    {
+      rc = -nread;
+      goto leave;
+    }
+  if (nread < 3)
+    {
+      rc = SPWQ_PROTOCOL_ERROR;
+      goto leave;
+    }
+      
+  if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
+    { /* we got a passphrase - convert it back from hex */
+      size_t pwlen = 0;
+      
+      for (i=3; i < nread && hexdigitp (pw+i); i+=2)
+        pw[pwlen++] = xtoi_2 (pw+i);
+      pw[pwlen] = 0; /* make a C String */
+      result = pw;
+      pw = NULL;
+    }
+  else if (nread > 7 && !memcmp (pw, "ERR 111", 7)
+      && (pw[7] == ' ' || pw[7] == '\n') )
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_info (_("canceled by user\n") );
+#endif
+      *errorcode = 0; /* canceled */
+    }
+  else 
+    {
+#ifdef SPWQ_USE_LOGGING
+      log_error (_("problem with the agent\n"));
+#endif
+      rc = SPWQ_ERR_RESPONSE;
+    }
+        
+ leave:
+  if (errorcode)
+    *errorcode = rc;
+  if (fd != -1)
+    close (fd);
+  if (pw)
+    spwq_free (pw);
+  return result;
+}
diff --git a/agent/simple-pwquery.h b/agent/simple-pwquery.h
new file mode 100644 (file)
index 0000000..a1b276f
--- /dev/null
@@ -0,0 +1,69 @@
+/* simple-pwquery.c - A simple password query cleint for gpg-agent
+ *     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
+ */
+
+#ifndef SIMPLE_PWQUERY_H
+#define SIMPLE_PWQUERY_H
+
+#ifdef SIMPLE_PWQUERY_IMPLEMENTATION /* Begin configuration stuff. */
+
+/* Include whatever files you need.  */
+#include <gcrypt.h>
+#include "../jnlib/logging.h"
+
+/* Try to write error message using the standard log mechanism.  The
+   current implementation requires that the HAVE_JNLIB_LOGGING is also
+   defined. */
+#define SPWQ_USE_LOGGING  1
+
+/* Memory allocation functions used by the implementation.  Note, that
+   the returned value is expected to be freed with
+   spwq_secure_free. */
+#define spwq_malloc(a)         gcry_malloc (a)
+#define spwq_free(a)           gcry_free (a)
+#define spwq_secure_malloc(a)  gcry_malloc_secure (a)
+#define spwq_secure_free(a)    gcry_free (a)
+
+
+#endif /*SIMPLE_PWQUERY_IMPLEMENTATION*/ /* End configuration stuff. */
+
+
+/* Ask the gpg-agent for a passphrase and present the user with a
+   DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
+   If a CACHEID is not NULL it is used to locate the passphrase in in
+   the cache and store it under this ID.  If ERRORCODE is not NULL it
+   should point a variable receiving an errorcode; thsi errocode might
+   be 0 if the user canceled the operation.  The function returns NULL
+   to indicate an error. */
+char *simple_pwquery (const char *cacheid, 
+                      const char *tryagain,
+                      const char *prompt,
+                      const char *description,
+                      int *errorcode);
+
+
+#define SPWQ_OUT_OF_CORE 1
+#define SPWQ_IO_ERROR 2
+#define SPWQ_PROTOCOL_ERROR 3 
+#define SPWQ_ERR_RESPONSE 4
+#define SPWQ_NO_AGENT 5
+#define SPWQ_SYS_ERROR 6
+#define SPWQ_GENERAL_ERROR 7
+
+#endif /*SIMPLE_PWQUERY_H*/