aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/ChangeLog27
-rw-r--r--agent/Makefile.am6
-rw-r--r--agent/agent.h2
-rw-r--r--agent/call-scd.c14
-rw-r--r--agent/command.c22
-rw-r--r--agent/divert-scd.c1
-rw-r--r--agent/findkey.c6
-rw-r--r--agent/gpg-agent.c15
-rw-r--r--agent/minip12.c2
-rw-r--r--agent/protect-tool.c147
-rw-r--r--agent/query.c14
-rw-r--r--agent/simple-pwquery.c485
-rw-r--r--agent/simple-pwquery.h69
13 files changed, 769 insertions, 41 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 3a37ec80b..cd1f04f5a 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,30 @@
+2002-06-27 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
* protect-tool.c (rsa_key_check): New.
diff --git a/agent/Makefile.am b/agent/Makefile.am
index baeab5d65..c814dd995 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -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)
diff --git a/agent/agent.h b/agent/agent.h
index 75683a0ad..ba6934366 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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;
diff --git a/agent/call-scd.c b/agent/call-scd.c
index 8b79e81cf..5f7de0578 100644
--- a/agent/call-scd.c
+++ b/agent/call-scd.c
@@ -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",
diff --git a/agent/command.c b/agent/command.c
index 387eef417..b98ec25ff 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -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);
}
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index f2a26acc7..dfc10087a 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -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);
diff --git a/agent/findkey.c b/agent/findkey.c
index 3a9dd53b8..fb3b36e4c 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -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;
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 7742e1b85..4441cf557 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -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("/"))
diff --git a/agent/minip12.c b/agent/minip12.c
index 29531b23b..6fe9d2c6c 100644
--- a/agent/minip12.c
+++ b/agent/minip12.c
@@ -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;
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index f53342595..4efe497a7 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -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;
+}
diff --git a/agent/query.c b/agent/query.c
index 724bbd57d..c7cba5e93 100644
--- a/agent/query.c
+++ b/agent/query.c
@@ -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
index 000000000..5bb08afec
--- /dev/null
+++ b/agent/simple-pwquery.c
@@ -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
index 000000000..a1b276ff6
--- /dev/null
+++ b/agent/simple-pwquery.h
@@ -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*/