diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/ChangeLog | 29 | ||||
-rw-r--r-- | agent/Makefile.am | 18 | ||||
-rw-r--r-- | agent/agent.h | 1 | ||||
-rw-r--r-- | agent/cache.c | 9 | ||||
-rw-r--r-- | agent/command.c | 61 | ||||
-rw-r--r-- | agent/gpg-agent.c | 5 | ||||
-rw-r--r-- | agent/preset-passphrase.c | 293 |
7 files changed, 406 insertions, 10 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index cf3569264..d835c8e60 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,8 +1,33 @@ 2004-12-21 Werner Koch <wk@g10code.com> + * preset-passphrase.c (preset_passphrase): Handle --passphrase. + + * Makefile.am (gpg_preset_passphrase_LDADD): Reorder libs so that + pwquery may use stuff from jnlib. Conditionally add -lwsock2 + (gpg_protect_tool_LDADD): Ditto. + + * preset-passphrase.c (main): Use default_homedir(). + (main) [W32]: Initialize sockets. + +2004-12-21 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (libexec_PROGRAMS): Add gpg-preset-passphrase. + (gpg_preset_passphrase_SOURCES, gpg_preset_passphrase_LDADD): New + targets. + * agent.h (opt): New member allow_cache_passphrase. + * cache.c (housekeeping): Check if R->ttl is not negative. + (agent_put_cache): Allow ttl to be negative. + * command.c (parse_hexstring): Allow something to follow the + hexstring. + (cmd_cache_passphrase): New function. + (register_commands): Add it. + * gpg-agent.c: Handle --allow-preset-passphrase. + * preset-passphrase.c: New file. + +2004-12-21 Werner Koch <wk@g10code.com> + * gpg-agent.c (main): Use default_homedir(). - * protect-tool.c (main): Ditto. - + * protect-tool.c (main): Ditto. 2004-12-20 Werner Koch <wk@g10code.com> diff --git a/agent/Makefile.am b/agent/Makefile.am index 6f3ea70d2..4cedbe74e 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 -libexec_PROGRAMS = gpg-protect-tool +libexec_PROGRAMS = gpg-protect-tool gpg-preset-passphrase AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl @@ -53,8 +53,20 @@ gpg_protect_tool_SOURCES = \ protect.c \ minip12.c minip12.h -gpg_protect_tool_LDADD = ../jnlib/libjnlib.a \ - ../common/libcommon.a ../common/libsimple-pwquery.a \ +gpg_protect_tool_LDADD = ../common/libsimple-pwquery.a \ + ../jnlib/libjnlib.a ../common/libcommon.a \ $(LIBGCRYPT_LIBS) -lgpg-error @LIBINTL@ +if HAVE_W32_SYSTEM +gpg_protect_tool_LDADD += -lwsock32 +endif +gpg_preset_passphrase_SOURCES = \ + preset-passphrase.c + +gpg_preset_passphrase_LDADD = ../common/libsimple-pwquery.a \ + ../jnlib/libjnlib.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) -lgpg-error @LIBINTL@ +if HAVE_W32_SYSTEM +gpg_preset_passphrase_LDADD += -lwsock32 +endif diff --git a/agent/agent.h b/agent/agent.h index 241b37b05..7d6bf9f47 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -63,6 +63,7 @@ struct { int ignore_cache_for_signing; int allow_mark_trusted; + int allow_preset_passphrase; int keep_tty; /* don't switch the TTY (for pinentry) on request */ int keep_display; /* don't switch the DISPLAY (for pinentry) on request */ } opt; diff --git a/agent/cache.c b/agent/cache.c index 8017b1414..b6762edd0 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -39,7 +39,7 @@ struct cache_item_s { ITEM next; time_t created; time_t accessed; - int ttl; /* max. lifetime given in seonds */ + int ttl; /* max. lifetime given in seonds, -1 one means infinite */ int lockcount; struct secret_data_s *pw; char key[1]; @@ -88,7 +88,8 @@ housekeeping (void) /* first expire the actual data */ for (r=thecache; r; r = r->next) { - if (!r->lockcount && r->pw && r->accessed + r->ttl < current) + if (!r->lockcount && r->pw + && r->ttl >= 0 && r->accessed + r->ttl < current) { if (DBG_CACHE) log_debug (" expired `%s' (%ds after last access)\n", @@ -118,7 +119,7 @@ housekeeping (void) Expire old and unused entries after 30 minutes */ for (rprev=NULL, r=thecache; r; ) { - if (!r->pw && r->accessed + 60*30 < current) + if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current) { if (r->lockcount) { @@ -194,7 +195,7 @@ agent_put_cache (const char *key, const char *data, int ttl) log_debug ("agent_put_cache `%s'\n", key); housekeeping (); - if (ttl < 1) + if (ttl == 1) ttl = opt.def_cache_ttl; if (!ttl) return 0; diff --git a/agent/command.c b/agent/command.c index 9fc08e211..dc8a4a158 100644 --- a/agent/command.c +++ b/agent/command.c @@ -141,7 +141,7 @@ parse_hexstring (ASSUAN_CONTEXT ctx, const char *string, size_t *len) /* parse the hash value */ for (p=string, n=0; hexdigitp (p); p++, n++) ; - if (*p) + if (*p != ' ' && *p != '\t' && *p) return set_error (Parameter_Error, "invalid hexstring"); if ((n&1)) return set_error (Parameter_Error, "odd number of digits"); @@ -741,6 +741,64 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line) return map_to_assuan_status (rc); } +/* PRESET_PASSPHRASE <hexstring_with_keygrip> <timeout> <passwd> + + Set the cached passphrase/PIN for the key identified by the keygrip + to passwd for the given time, where -1 means infinite and 0 means + the default (currently only a timeout of -1 is allowed, which means + to never expire it). If passwd is not provided, ask for it via the + pinentry module. */ +static int +cmd_preset_passphrase (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + unsigned char grip[20]; + char *grip_clear = NULL; + char *passphrase = NULL; + int ttl; + + if (!opt.allow_preset_passphrase) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + rc = parse_keygrip (ctx, line, grip); + if (rc) + return rc; + + /* FIXME: parse_keygrip should return a tail pointer. */ + grip_clear = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return map_to_assuan_status (gpg_error (GPG_ERR_MISSING_VALUE)); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + + /* Currently, only infinite timeouts are allowed. */ + ttl = -1; + if (line[0] != '-' || line[1] != '1') + return map_to_assuan_status (gpg_error (GPG_ERR_NOT_IMPLEMENTED)); + line++; + line++; + while (!(*line != ' ' && *line != '\t')) + line++; + + /* If there is a passphrase, use it. Currently, a passphrase is + required. */ + if (*line) + passphrase = line; + else + return map_to_assuan_status (gpg_error (GPG_ERR_NOT_IMPLEMENTED)); + + rc = agent_put_cache (grip_clear, passphrase, ttl); + + if (rc) + log_error ("command preset_passwd failed: %s\n", gpg_strerror (rc)); + + return map_to_assuan_status (rc); +} + /* SCD <commands to pass to the scdaemon> @@ -837,6 +895,7 @@ register_commands (ASSUAN_CONTEXT ctx) { "PKDECRYPT", cmd_pkdecrypt }, { "GENKEY", cmd_genkey }, { "GET_PASSPHRASE", cmd_get_passphrase }, + { "PRESET_PASSPHRASE", cmd_preset_passphrase }, { "CLEAR_PASSPHRASE", cmd_clear_passphrase }, { "GET_CONFIRMATION", cmd_get_confirmation }, { "LISTTRUSTED", cmd_listtrusted }, diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 2c3d834a5..e76623f75 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -89,6 +89,7 @@ enum cmd_and_opt_values oIgnoreCacheForSigning, oAllowMarkTrusted, + oAllowPresetPassphrase, oKeepTTY, oKeepDISPLAY }; @@ -141,6 +142,8 @@ static ARGPARSE_OPTS opts[] = { N_("do not use the PIN cache when signing")}, { oAllowMarkTrusted, "allow-mark-trusted", 0, N_("allow clients to mark keys as \"trusted\"")}, + { oAllowPresetPassphrase, "allow-preset-passphrase", 0, + N_("allow presetting passphrase")}, {0} }; @@ -392,6 +395,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break; + case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break; + default: return 0; /* not handled */ } diff --git a/agent/preset-passphrase.c b/agent/preset-passphrase.c new file mode 100644 index 000000000..6a9f07a3e --- /dev/null +++ b/agent/preset-passphrase.c @@ -0,0 +1,293 @@ +/* preset-passphrase.c - A tool to preset a passphrase. + * Copyright (C) 2004 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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <unistd.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif +#ifdef HAVE_DOSISH_SYSTEM +#include <fcntl.h> /* for setmode() */ +#endif +#ifdef HAVE_W32_SYSTEM +#include <windows.h> /* To initialize the sockets. fixme */ +#endif + +#define JNLIB_NEED_LOG_LOGV +#include "agent.h" +#include "minip12.h" +#include "simple-pwquery.h" +#include "i18n.h" +#include "sysutils.h" + + +enum cmd_and_opt_values +{ aNull = 0, + oVerbose = 'v', + oPassphrase = 'P', + + oPreset = 'c', + oForget = 'f', + + oNoVerbose = 500, + + oHomedir, + +aTest }; + + +static const char *opt_homedir; +static const char *opt_passphrase; + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oVerbose, "verbose", 0, "verbose" }, + { oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" }, + { oPreset, "preset", 256, "preset passphrase"}, + { oForget, "forget", 256, "forget passphrase"}, + + { oHomedir, "homedir", 2, "@" }, + {0} +}; + + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "gpg-preset-passphrase (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: + p = _("Usage: gpg-preset-passphrase [options] KEYGRIP (-h for help)\n"); + break; + case 41: + p = _("Syntax: gpg-preset-passphrase [options] KEYGRIP\n" + "Password cache maintenance\n"); + break; + + default: p = NULL; + } + return p; +} + + + +static void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE_GT ); +#else +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +#endif +#endif +} + + +static gpg_error_t +map_spwq_error (int err) +{ + switch (err) + { + case 0: + return 0; + case SPWQ_OUT_OF_CORE: + return gpg_error_from_errno (ENOMEM); + case SPWQ_IO_ERROR: + return gpg_error_from_errno (EIO); + case SPWQ_PROTOCOL_ERROR: + return gpg_error (GPG_ERR_PROTOCOL_VIOLATION); + case SPWQ_ERR_RESPONSE: + return gpg_error (GPG_ERR_INV_RESPONSE); + case SPWQ_NO_AGENT: + return gpg_error (GPG_ERR_NO_AGENT); + case SPWQ_SYS_ERROR: + return gpg_error_from_errno (errno); + case SPWQ_GENERAL_ERROR: + default: + return gpg_error (GPG_ERR_GENERAL); + } +} + + +static void +preset_passphrase (const char *keygrip) +{ + int rc; + char *line; + /* FIXME: Use secure memory. */ + char passphrase[500]; + + if (!opt_passphrase) + { + rc = read (0, passphrase, sizeof (passphrase) - 1); + if (rc < 0) + { + log_error ("reading passphrase failed: %s\n", + gpg_strerror (gpg_error_from_errno (errno))); + return; + } + passphrase[rc] = '\0'; + line = strchr (passphrase, '\n'); + if (line) + { + line--; + if (line > passphrase && line[-1] == '\r') + line--; + *line = '\0'; + } + + /* FIXME: How to handle empty passwords? */ + } + + rc = asprintf (&line, "PRESET_PASSPHRASE %s -1 %s\n", keygrip, + opt_passphrase? opt_passphrase : passphrase); + if (rc < 0) + { + log_error ("caching passphrase failed: %s\n", + gpg_strerror (gpg_error_from_errno (errno))); + return; + } + if (!opt_passphrase) + wipememory (passphrase, sizeof (passphrase)); + + rc = map_spwq_error (simple_query (line)); + if (rc) + { + log_error ("caching passphrase failed: %s\n", gpg_strerror (rc)); + return; + } + + wipememory (line, strlen (line)); + free (line); +} + + +static void +forget_passphrase (const char *keygrip) +{ + int rc; + char *line; + + rc = asprintf (&line, "CLEAR_PASSPHRASE %s\n", keygrip); + if (rc < 0) + { + log_error ("clearing passphrase failed: %s\n", + gpg_strerror (gpg_error_from_errno (errno))); + return; + } + free (line); +} + + +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int cmd = 0; + const char *keygrip = NULL; + + set_strusage (my_strusage); + log_set_prefix ("gpg-preset-passphrase", 1); + + /* Try to auto set the character set. */ + set_native_charset (NULL); + +#ifdef HAVE_W32_SYSTEM + /* Fixme: Need to initialize the Windows sockets: This should be + moved to another place and we should make sure that it won't get + doen twice, like when Pth is used too. */ + { + WSADATA wsadat; + WSAStartup (0x202, &wsadat); + } +#endif + + i18n_init (); + + opt_homedir = default_homedir (); + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* (do not remove the args) */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oHomedir: opt_homedir = pargs.r.ret_str; break; + + case oPreset: cmd = oPreset; break; + case oForget: cmd = oForget; break; + case oPassphrase: opt_passphrase = pargs.r.ret_str; break; + + default : pargs.err = 2; break; + } + } + if (log_get_errorcount(0)) + exit(2); + + if (argc == 1) + keygrip = *argv; + else + usage (1); + + if (cmd == oPreset) + preset_passphrase (keygrip); + else if (cmd == oForget) + forget_passphrase (keygrip); + else + log_error ("one of the options --preset or --forget must be given\n"); + + agent_exit (0); + return 8; /*NOTREACHED*/ +} + + +void +agent_exit (int rc) +{ + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} |