Import/export of pkcs#12 now uses the gpg-agent directly.

Removed duplicated code (percent unescaping).
This commit is contained in:
Werner Koch 2009-04-01 10:51:53 +00:00
parent 98e1a75e20
commit f8b4cd7650
29 changed files with 716 additions and 369 deletions

3
NEWS
View File

@ -19,6 +19,9 @@ Noteworthy changes in version 2.0.12 (not released)
* Support for the Telesec Netkey 3 cards.
* The gpg-protect-tool now uses gpg-agent via libassuan. Under
Windows the Pinentry will now be put into the foreground.
Noteworthy changes in version 2.0.11 (2009-03-03)
-------------------------------------------------

View File

@ -1,3 +1,21 @@
2009-04-01 Werner Koch <wk@g10code.com>
* protect-tool.c (pe_opt): New.
(opts): Add option --agent-program. Use ARGPARSE macros.
(get_new_passphrase): Remove.
(get_passphrase): Use gpg-agent directly. Remove arg OPT_CHECK and
change all callers.
* Makefile.am (gpg_protect_tool_LDADD): Replace pwquery_libs by
LIBASSUAN_LIBS.
(gpg_protect_tool_CFLAGS): New.
* command.c (percent_plus_unescape): Remove.
(cmd_putval): Use percent_plus_unescape_inplace.
* call-scd.c (unescape_status_string): Remove.
(card_getattr_cb): Use percent_plus_unescape.
* protect-tool.c (main): Use percent_plus_unescape from common/.
(percent_plus_unescape, percent_plus_unescape_string): Remove.
2009-03-27 Werner Koch <wk@g10code.com>
* learncard.c (agent_handle_learn): Add new certtype 111.

View File

@ -74,9 +74,9 @@ gpg_protect_tool_SOURCES = \
protect.c \
minip12.c minip12.h
# Needs $(NETLIBS) for libsimple-pwquery.la.
gpg_protect_tool_LDADD = $(pwquery_libs) $(common_libs) \
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)
gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS)
gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
$(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)
gpg_preset_passphrase_SOURCES = \
preset-passphrase.c

View File

@ -105,6 +105,7 @@ static void
dump_mutex_state (pth_mutex_t *m)
{
#ifdef _W32_PTH_H
(void)m;
log_printf ("unknown under W32");
#else
if (!(m->mx_state & PTH_MUTEX_INITIALIZED))

View File

@ -136,6 +136,7 @@ static void
dump_mutex_state (pth_mutex_t *m)
{
#ifdef _W32_PTH_H
(void)m;
log_printf ("unknown under W32");
#else
if (!(m->mx_state & PTH_MUTEX_INITIALIZED))
@ -561,43 +562,6 @@ agent_reset_scd (ctrl_t ctrl)
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary Nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static char *
unescape_status_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xtrymalloc (strlen ((const char*)s)+1);
if (!buffer)
return NULL;
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
}
static int
learn_status_cb (void *opaque, const char *line)
@ -1045,7 +1009,7 @@ card_getattr_cb (void *opaque, const char *line)
if (keywordlen == parm->keywordlen
&& !memcmp (keyword, parm->keyword, keywordlen))
{
parm->data = unescape_status_string ((const unsigned char*)line);
parm->data = percent_plus_unescape ((const unsigned char*)line, 0xff);
if (!parm->data)
parm->error = errno;
}

View File

@ -236,42 +236,6 @@ plus_to_blank (char *s)
}
/* Do the percent and plus/space unescaping in place and return the
length of the valid buffer. */
static size_t
percent_plus_unescape (char *string)
{
unsigned char *p = (unsigned char *)string;
size_t n = 0;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
*p++ = xtoi_2 (string);
n++;
string+= 2;
}
else if (*string == '+')
{
*p++ = ' ';
n++;
string++;
}
else
{
*p++ = *string++;
n++;
}
}
return n;
}
/* Parse a hex string. Return an Assuan error code or 0 on success and the
length of the parsed string in LEN. */
static int
@ -1494,7 +1458,7 @@ cmd_putval (assuan_context_t ctx, char *line)
p = strchr (value, ' ');
if (p)
*p = 0;
valuelen = percent_plus_unescape (value);
valuelen = percent_plus_unescape_inplace (value, 0);
}
}
if (!key || !*key)

View File

@ -41,13 +41,14 @@
#define JNLIB_NEED_LOG_LOGV
#include "agent.h"
#include "minip12.h"
#include "simple-pwquery.h"
#include "i18n.h"
#include "get-passphrase.h"
#include "sysutils.h"
enum cmd_and_opt_values
{ aNull = 0,
{
aNull = 0,
oVerbose = 'v',
oArmor = 'a',
oPassphrase = 'P',
@ -72,17 +73,19 @@ enum cmd_and_opt_values
oPrompt,
oStatusMsg,
aTest };
oAgentProgram
};
struct rsa_secret_key_s
{
gcry_mpi_t n; /* public modulus */
gcry_mpi_t e; /* public exponent */
gcry_mpi_t d; /* exponent */
gcry_mpi_t p; /* prime p. */
gcry_mpi_t q; /* prime q. */
gcry_mpi_t u; /* inverse of p mod q. */
};
{
gcry_mpi_t n; /* public modulus */
gcry_mpi_t e; /* public exponent */
gcry_mpi_t d; /* exponent */
gcry_mpi_t p; /* prime p. */
gcry_mpi_t q; /* prime q. */
gcry_mpi_t u; /* inverse of p mod q. */
};
static const char *opt_homedir;
@ -96,41 +99,51 @@ static const char *opt_passphrase;
static char *opt_prompt;
static int opt_status_msg;
static const char *opt_p12_charset;
static const char *opt_agent_program;
static char *get_passphrase (int promptno, int opt_check);
static char *get_new_passphrase (int promptno);
static char *get_passphrase (int promptno);
static void release_passphrase (char *pw);
static int store_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force);
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (oProtect, "protect", "protect a private key"),
ARGPARSE_c (oUnprotect, "unprotect", "unprotect a private key"),
ARGPARSE_c (oShadow, "shadow", "create a shadow entry for a public key"),
ARGPARSE_c (oShowShadowInfo, "show-shadow-info", "return the shadow info"),
ARGPARSE_c (oShowKeygrip, "show-keygrip", "show the \"keygrip\""),
ARGPARSE_c (oP12Import, "p12-import",
"import a pkcs#12 encoded private key"),
ARGPARSE_c (oP12Export, "p12-export",
"export a private key pkcs#12 encoded"),
{ 301, NULL, 0, N_("@Options:\n ") },
ARGPARSE_group (301, N_("@\nOptions:\n ")),
{ oVerbose, "verbose", 0, "verbose" },
{ oArmor, "armor", 0, "write output in advanced format" },
{ oCanonical, "canonical", 0, "write output in canonical format" },
{ oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" },
{ oProtect, "protect", 256, "protect a private key"},
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
{ oShadow, "shadow", 256, "create a shadow entry for a public key"},
{ oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"},
{ oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""},
ARGPARSE_s_n (oVerbose, "verbose", "verbose"),
ARGPARSE_s_n (oArmor, "armor", "write output in advanced format"),
ARGPARSE_s_n (oCanonical, "canonical", "write output in canonical format"),
{ oP12Import, "p12-import", 256, "import a pkcs#12 encoded private key"},
{ oP12Export, "p12-export", 256, "export a private key pkcs#12 encoded"},
{ oP12Charset,"p12-charset", 2,
"|NAME|set charset for a new PKCS#12 passphrase to NAME" },
{ oHaveCert, "have-cert", 0, "certificate to export provided on STDIN"},
{ oStore, "store", 0, "store the created key in the appropriate place"},
{ oForce, "force", 0, "force overwriting"},
{ oNoFailOnExist, "no-fail-on-exist", 0, "@" },
{ oHomedir, "homedir", 2, "@" },
{ oPrompt, "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"},
{ oStatusMsg, "enable-status-msg", 0, "@"},
ARGPARSE_s_s (oPassphrase, "passphrase", "|STRING|use passphrase STRING"),
ARGPARSE_s_s (oP12Charset,"p12-charset",
"|NAME|set charset for a new PKCS#12 passphrase to NAME"),
ARGPARSE_s_n (oHaveCert, "have-cert",
"certificate to export provided on STDIN"),
ARGPARSE_s_n (oStore, "store",
"store the created key in the appropriate place"),
ARGPARSE_s_n (oForce, "force",
"force overwriting"),
ARGPARSE_s_n (oNoFailOnExist, "no-fail-on-exist", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oPrompt, "prompt",
"|ESCSTRING|use ESCSTRING as prompt in pinentry"),
ARGPARSE_s_n (oStatusMsg, "enable-status-msg", "@"),
{0}
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
ARGPARSE_end ()
};
static const char *
@ -158,9 +171,6 @@ my_strusage (int level)
}
/* Include the implementation of map_spwq_error. */
MAP_SPWQ_ERROR_IMPL
/* static void */
/* print_mpi (const char *text, gcry_mpi_t a) */
/* { */
@ -333,7 +343,7 @@ read_and_protect (const char *fname)
if (!key)
return;
pw = get_passphrase (1, 0);
pw = get_passphrase (1);
rc = agent_protect (key, pw, &result, &resultlen);
release_passphrase (pw);
xfree (key);
@ -372,7 +382,7 @@ read_and_unprotect (const char *fname)
if (!key)
return;
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
rc = agent_unprotect (key, (pw=get_passphrase (1)),
protected_at, &result, &resultlen);
release_passphrase (pw);
xfree (key);
@ -678,7 +688,7 @@ import_p12_file (const char *fname)
buf_off = 0;
kparms = p12_parse ((unsigned char*)buf+buf_off, buflen-buf_off,
(pw=get_passphrase (2, 0)),
(pw=get_passphrase (2)),
import_p12_cert_cb, NULL);
release_passphrase (pw);
xfree (buf);
@ -753,12 +763,8 @@ import_p12_file (const char *fname)
assert (buflen);
gcry_sexp_release (s_key);
pw = get_new_passphrase (4);
if (!pw)
rc = gpg_error (GPG_ERR_CANCELED);
else
rc = agent_protect (key, pw, &result, &resultlen);
pw = get_passphrase (4);
rc = agent_protect (key, pw, &result, &resultlen);
release_passphrase (pw);
xfree (key);
if (rc)
@ -896,7 +902,7 @@ export_p12_file (const char *fname)
unsigned char *tmpkey;
size_t tmplen;
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
rc = agent_unprotect (key, (pw=get_passphrase (1)),
NULL, &tmpkey, &tmplen);
release_passphrase (pw);
if (rc)
@ -985,11 +991,8 @@ export_p12_file (const char *fname)
kparms[7] = sk.u;
kparms[8] = NULL;
pw = get_new_passphrase (3);
if (!pw)
key = NULL;
else
key = p12_build (kparms, cert, certlen, pw, opt_p12_charset, &keylen);
pw = get_passphrase (3);
key = p12_build (kparms, cert, certlen, pw, opt_p12_charset, &keylen);
release_passphrase (pw);
xfree (cert);
for (i=0; i < 8; i++)
@ -1005,54 +1008,6 @@ export_p12_file (const char *fname)
}
/* Do the percent and plus/space unescaping in place and return the
length of the valid buffer. */
static size_t
percent_plus_unescape (unsigned char *string)
{
unsigned char *p = string;
size_t n = 0;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
*p++ = xtoi_2 (string);
n++;
string+= 2;
}
else if (*string == '+')
{
*p++ = ' ';
n++;
string++;
}
else
{
*p++ = *string++;
n++;
}
}
return n;
}
/* Remove percent and plus escaping and make sure that the reuslt is a
string. This is done in place. Returns STRING. */
static char *
percent_plus_unescape_string (char *string)
{
unsigned char *p = (unsigned char*)string;
size_t n;
n = percent_plus_unescape (p);
p[n] = 0;
return string;
}
int
main (int argc, char **argv )
@ -1094,6 +1049,8 @@ main (int argc, char **argv )
case oCanonical: opt_canonical=1; break;
case oHomedir: opt_homedir = pargs.r.ret_str; break;
case oAgentProgram: opt_agent_program = pargs.r.ret_str; break;
case oProtect: cmd = oProtect; break;
case oUnprotect: cmd = oUnprotect; break;
case oShadow: cmd = oShadow; break;
@ -1111,11 +1068,11 @@ main (int argc, char **argv )
case oPrompt: opt_prompt = pargs.r.ret_str; break;
case oStatusMsg: opt_status_msg = 1; break;
default : pargs.err = 2; break;
default: pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
if (log_get_errorcount(0))
exit(2);
if (log_get_errorcount (0))
exit (2);
fname = "-";
if (argc == 1)
@ -1123,15 +1080,15 @@ main (int argc, char **argv )
else if (argc > 1)
usage (1);
/* Tell simple-pwquery about the the standard socket name. */
{
char *tmp = make_filename (opt_homedir, "S.gpg-agent", NULL);
simple_pw_set_socket (tmp);
xfree (tmp);
}
/* Set the information which can't be taken from envvars. */
gnupg_prepare_get_passphrase (GPG_ERR_SOURCE_DEFAULT,
opt.verbose,
opt_homedir,
opt_agent_program,
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (opt_prompt)
opt_prompt = percent_plus_unescape_string (xstrdup (opt_prompt));
opt_prompt = percent_plus_unescape (opt_prompt, 0);
if (cmd == oProtect)
read_and_protect (fname);
@ -1169,102 +1126,65 @@ agent_exit (int rc)
2 = for unprotecting a pkcs#12 object
3 = for protecting a new pkcs#12 object
4 = for protecting an imported pkcs#12 in our system
5 = reenter the passphrase
When adding 100 to the values, a "does not match - try again" error
message is shown.
*/
static char *
get_passphrase (int promptno, int opt_check)
get_passphrase (int promptno)
{
char *pw;
int err;
const char *desc;
char *orig_codeset;
int error_msgno;
int repeat = 0;
if (opt_passphrase)
return xstrdup (opt_passphrase);
error_msgno = promptno / 100;
promptno %= 100;
orig_codeset = i18n_switchto_utf8 ();
if (promptno == 1 && opt_prompt)
desc = opt_prompt;
{
desc = opt_prompt;
}
else if (promptno == 2)
desc = _("Please enter the passphrase to unprotect the "
"PKCS#12 object.");
{
desc = _("Please enter the passphrase to unprotect the "
"PKCS#12 object.");
}
else if (promptno == 3)
desc = _("Please enter the passphrase to protect the "
"new PKCS#12 object.");
{
desc = _("Please enter the passphrase to protect the "
"new PKCS#12 object.");
repeat = 1;
}
else if (promptno == 4)
desc = _("Please enter the passphrase to protect the "
"imported object within the GnuPG system.");
else if (promptno == 5)
desc = _("Please re-enter this passphrase");
{
desc = _("Please enter the passphrase to protect the "
"imported object within the GnuPG system.");
repeat = 1;
}
else
desc = _("Please enter the passphrase or the PIN\n"
"needed to complete this operation.");
pw = simple_pwquery (NULL,
error_msgno == 1? _("does not match - try again"):NULL,
_("Passphrase:"), desc, opt_check, &err);
err = map_spwq_error (err);
i18n_switchback (orig_codeset);
if (!pw)
err = gnupg_get_passphrase (NULL, NULL, _("Passphrase:"), desc,
repeat, repeat, 1, &pw);
if (err)
{
if (err)
if (gpg_err_code (err) == GPG_ERR_CANCELED)
log_info (_("cancelled\n"));
else
log_error (_("error while asking for the passphrase: %s\n"),
gpg_strerror (err));
else
log_info (_("cancelled\n"));
agent_exit (0);
}
assert (pw);
return pw;
}
/* Same as get_passphrase but requests it a second time and compares
it to the one entered the first time. */
static char *
get_new_passphrase (int promptno)
{
char *pw;
int i, secondpromptno;
pw = get_passphrase (promptno, 1);
if (!pw)
return NULL; /* Canceled. */
if (!*pw)
return pw; /* Empty passphrase - no need to ask for repeating it. */
secondpromptno = 5;
for (i=0; i < 3; i++)
{
char *pw2 = get_passphrase (secondpromptno, 0);
if (!pw2)
{
xfree (pw);
return NULL; /* Canceled. */
}
if (!strcmp (pw, pw2))
{
xfree (pw2);
return pw; /* Okay. */
}
secondpromptno = 105;
xfree (pw2);
}
xfree (pw);
return NULL; /* 3 times repeated wrong - cancel. */
}
static void
release_passphrase (char *pw)
{

View File

@ -1,3 +1,22 @@
2009-04-01 Werner Koch <wk@g10code.com>
* exechelp.c (gnupg_spawn_process): Implement new flag bit 6.
* sysutils.c (gnupg_allow_set_foregound_window): Allow the use of
ASFW_ANY.
* membuf.c (put_membuf, get_membuf): Wipe memory on out of core.
2009-03-31 Werner Koch <wk@g10code.com>
* percent.c (percent_unescape, percent_plus_unescape): New.
(percent_plus_unescape_inplace, percent_unescape_inplace): New.
(do_plus_or_plain_unescape, count_unescape, do_unescape): New.
(do_unescape_inplace): New.
* t-percent.c (test_percent_plus_escape): Test percent_plus_unescape.
* get-passphrase.c, get-passphrase.h: New.
* Makefile.am (without_pth_sources): New.
2009-03-18 Werner Koch <wk@g10code.com>
* exechelp.c: Include sys/resource.h and sys/stat.h.

View File

@ -71,9 +71,12 @@ common_sources = \
localename.c \
helpfile.c
# Sources only useful without PTH.
without_pth_sources = \
get-passphrase.c get-passphrase.h
libcommon_a_SOURCES = $(common_sources)
libcommon_a_SOURCES = $(common_sources) $(without_pth_sources)
if USE_DNS_SRV
libcommon_a_SOURCES += srv.c
endif

View File

@ -52,6 +52,7 @@
#include "util.h"
#include "i18n.h"
#include "sysutils.h"
#include "exechelp.h"
/* Define to 1 do enable debugging. */
@ -471,6 +472,10 @@ gnupg_create_inbound_pipe (int filedes[2])
This flag is only useful under W32 systems, so that no new
console is created and pops up a console window when
starting the server
Bit 6: On W32 run AllowSetForegroundWindow for the child. Due to
error problems this actually allows SetForegroundWindow for
childs of this process.
Returns 0 on success or an error code. */
gpg_error_t
@ -568,6 +573,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
/* " dwProcessID=%d dwThreadId=%d\n", */
/* pi.hProcess, pi.hThread, */
/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
/* Fixme: For unknown reasons AllowSetForegroundWindow returns an
invalid argument error if we pass the the correct processID to
it. As a workaround we use -1 (ASFW_ANY). */
if ( (flags & 64) )
gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
/* Process has been created suspended; resume it now. */
ResumeThread (pi.hThread);

277
common/get-passphrase.c Normal file
View File

@ -0,0 +1,277 @@
/* get-passphrase.c - Ask for a passphrase via the agent
* Copyright (C) 2009 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <assuan.h>
#include "util.h"
#include "i18n.h"
#include "asshelp.h"
#include "membuf.h"
#include "sysutils.h"
#include "get-passphrase.h"
/* The context used by this process to ask for the passphrase. */
static assuan_context_t agent_ctx;
static struct
{
gpg_err_source_t errsource;
int verbosity;
const char *homedir;
const char *agent_program;
const char *display;
const char *ttyname;
const char *ttytype;
const char *lc_ctype;
const char *lc_messages;
const char *xauthority;
const char *pinentry_user_data;
} agentargs;
/* Set local variable to be used for a possible agent startup. Note
that the strings are just pointers and should not anymore be
modified by the caller. */
void
gnupg_prepare_get_passphrase (gpg_err_source_t errsource,
int verbosity,
const char *homedir,
const char *agent_program,
const char *opt_display,
const char *opt_ttyname,
const char *opt_ttytype,
const char *opt_lc_ctype,
const char *opt_lc_messages,
const char *opt_xauthority,
const char *opt_pinentry_user_data)
{
agentargs.errsource = errsource;
agentargs.verbosity = verbosity;
agentargs.homedir = homedir;
agentargs.agent_program = agent_program;
agentargs.display = opt_display;
agentargs.ttyname = opt_ttyname;
agentargs.ttytype = opt_ttytype;
agentargs.lc_ctype = opt_lc_ctype;
agentargs.lc_messages = opt_lc_messages;
agentargs.xauthority = opt_xauthority;
agentargs.pinentry_user_data = opt_pinentry_user_data;
}
/* Try to connect to the agent via socket or fork it off and work by
pipes. Handle the server's initial greeting. */
static gpg_error_t
start_agent (void)
{
gpg_error_t err;
/* Fixme: This code is not thread safe, thus we don't build it with
pth. We will need a context for each thread or serialize the
access to the agent. */
if (agent_ctx)
return 0;
err = start_new_gpg_agent (&agent_ctx,
agentargs.errsource,
agentargs.homedir,
agentargs.agent_program,
agentargs.display,
agentargs.ttyname,
agentargs.ttytype,
agentargs.lc_ctype,
agentargs.lc_messages,
agentargs.xauthority,
agentargs.pinentry_user_data,
agentargs.verbosity, 0, NULL, NULL);
if (!err)
{
/* Tell the agent that we support Pinentry notifications. No
error checking so that it will work with older agents. */
assuan_transact (agent_ctx, "OPTION allow-pinentry-notify",
NULL, NULL, NULL, NULL, NULL, NULL);
}
return err;
}
/* This is the default inquiry callback. It merely handles the
Pinentry notification. */
static int
default_inq_cb (void *opaque, const char *line)
{
(void)opaque;
if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
{
gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
/* We do not return errors to avoid breaking other code. */
}
else
log_debug ("ignoring gpg-agent inquiry `%s'\n", line);
return 0;
}
static int
membuf_data_cb (void *opaque, const void *buffer, size_t length)
{
membuf_t *data = opaque;
if (buffer)
put_membuf (data, buffer, length);
return 0;
}
/* Ask for a passphrase via gpg-agent. On success the caller needs to
free the string stored at R_PASSPHRASE. On error NULL will be
stored at R_PASSPHRASE and an appropriate gpg error code is
returned. With REPEAT set to 1, gpg-agent will ask the user to
repeat the just entered passphrase. CACHE_ID is a gpg-agent style
passphrase cache id or NULL. ERR_MSG is a error message to be
presented to the user (e.g. "bad passphrase - try again") or NULL.
PROMPT is the prompt string to label the entry box, it may be NULL
for a default one. DESC_MSG is a longer description to be
displayed above the entry box, if may be NULL for a default one.
If USE_SECMEM is true, the returned passphrase is retruned in
secure memory. The length of all these strings is limited; they
need to fit in their encoded form into a standard Assuan line (i.e
less then about 950 characters). All strings shall be UTF-8. */
gpg_error_t
gnupg_get_passphrase (const char *cache_id,
const char *err_msg,
const char *prompt,
const char *desc_msg,
int repeat,
int check_quality,
int use_secmem,
char **r_passphrase)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
const char *arg1 = NULL;
char *arg2 = NULL;
char *arg3 = NULL;
char *arg4 = NULL;
membuf_t data;
*r_passphrase = NULL;
err = start_agent ();
if (err)
return err;
/* Check that the gpg-agent understands the repeat option. */
if (assuan_transact (agent_ctx,
"GETINFO cmd_has_option GET_PASSPHRASE repeat",
NULL, NULL, NULL, NULL, NULL, NULL))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
arg1 = cache_id && *cache_id? cache_id:NULL;
if (err_msg && *err_msg)
if (!(arg2 = percent_plus_escape (err_msg)))
goto no_mem;
if (prompt && *prompt)
if (!(arg3 = percent_plus_escape (prompt)))
goto no_mem;
if (desc_msg && *desc_msg)
if (!(arg4 = percent_plus_escape (desc_msg)))
goto no_mem;
snprintf (line, DIM(line)-1,
"GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
check_quality? "--check ":"",
repeat,
arg1? arg1:"X",
arg2? arg2:"X",
arg3? arg3:"X",
arg4? arg4:"X");
line[DIM(line)-1] = 0;
xfree (arg2);
xfree (arg3);
xfree (arg4);
if (use_secmem)
init_membuf_secure (&data, 64);
else
init_membuf (&data, 64);
err = assuan_transact (agent_ctx, line,
membuf_data_cb, &data,
default_inq_cb, NULL, NULL, NULL);
/* Older Pinentries return the old assuan error code for canceled
which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and
not to the code for a user cancel. Fix this here. */
if (err && gpg_err_source (err)
&& gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
if (err)
{
void *p;
size_t n;
p = get_membuf (&data, &n);
if (p)
wipememory (p, n);
xfree (p);
}
else
{
put_membuf (&data, "", 1);
*r_passphrase = get_membuf (&data, NULL);
if (!*r_passphrase)
err = gpg_error_from_syserror ();
}
return err;
no_mem:
err = gpg_error_from_syserror ();
xfree (arg2);
xfree (arg3);
xfree (arg4);
return err;
}
/* Flush the passphrase cache with Id CACHE_ID. */
gpg_error_t
gnupg_clear_passphrase (const char *cache_id)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
if (!cache_id || !*cache_id)
return 0;
err = start_agent ();
if (err)
return err;
snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
line[DIM(line)-1] = 0;
return assuan_transact (agent_ctx, line, NULL, NULL,
default_inq_cb, NULL, NULL, NULL);
}

47
common/get-passphrase.h Normal file
View File

@ -0,0 +1,47 @@
/* get-passphrase.h - Definitions to ask for a passphrase via the agent.
* Copyright (C) 2009 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GNUPG_COMMON_GET_PASSPHRASE_H
#define GNUPG_COMMON_GET_PASSPHRASE_H
void gnupg_prepare_get_passphrase (gpg_err_source_t errsource,
int verbosity,
const char *homedir,
const char *agent_program,
const char *opt_display,
const char *opt_ttyname,
const char *opt_ttytype,
const char *opt_lc_ctype,
const char *opt_lc_messages,
const char *opt_xauthority,
const char *opt_pinentry_user_data);
gpg_error_t gnupg_get_passphrase (const char *cache_id,
const char *err_msg,
const char *prompt,
const char *desc_msg,
int repeat,
int check_quality,
int use_secmem,
char **r_passphrase);
gpg_error_t gnupg_clear_passphrase (const char *cache_id);
#endif /*GNUPG_COMMON_GET_PASSPHRASE_H*/

View File

@ -1,5 +1,5 @@
/* membuf.c - A simple implementation of a dynamic buffer
* Copyright (C) 2001, 2003 Free Software Foundation, Inc.
/* membuf.c - A simple implementation of a dynamic buffer.
* Copyright (C) 2001, 2003, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -75,7 +75,7 @@ put_membuf (membuf_t *mb, const void *buf, size_t len)
in case we are storing sensitive data here. The membuf
API does not provide another way to cleanup after an
error. */
memset (mb->buf, 0, mb->len);
wipememory (mb->buf, mb->len);
return;
}
mb->buf = p;
@ -99,8 +99,12 @@ get_membuf (membuf_t *mb, size_t *len)
if (mb->out_of_core)
{
xfree (mb->buf);
mb->buf = NULL;
if (mb->buf)
{
wipememory (mb->buf, mb->len);
xfree (mb->buf);
mb->buf = NULL;
}
errno = mb->out_of_core;
return NULL;
}

View File

@ -1,5 +1,5 @@
/* percent.c - Percent escaping
* Copyright (C) 2008 Free Software Foundation, Inc.
* Copyright (C) 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -21,6 +21,7 @@
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include "util.h"
@ -74,3 +75,155 @@ percent_plus_escape (const char *string)
return buffer;
}
/* Do the percent and plus/space unescaping from STRING to BUFFER and
return the length of the valid buffer. Plus unescaping is only
done if WITHPLUS is true. An escaped Nul character will be
replaced by NULREPL. */
static size_t
do_unescape (unsigned char *buffer, const unsigned char *string,
int withplus, int nulrepl)
{
unsigned char *p = buffer;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
*p = xtoi_2 (string);
if (!*p)
*p = nulrepl;
string++;
}
else if (*string == '+' && withplus)
*p = ' ';
else
*p = *string;
p++;
string++;
}
return (p - buffer);
}
/* Count space required after unescaping STRING. Note that this will
never be larger than strlen (STRING). */
static size_t
count_unescape (const unsigned char *string)
{
size_t n = 0;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
string++;
}
string++;
n++;
}
return n;
}
/* Helper. */
static char *
do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl)
{
size_t nbytes, n;
char *newstring;
nbytes = count_unescape (string);
newstring = xtrymalloc (nbytes+1);
if (newstring)
{
n = do_unescape (newstring, string, withplus, nulrepl);
assert (n == nbytes);
newstring[n] = 0;
}
return newstring;
}
/* Create a new allocated string from STRING with all "%xx" sequences
decoded and all plus signs replaced by a space. Embedded Nul
characters are replaced by the value of NULREPL. The function
returns the new string or NULL in case of a malloc failure. */
char *
percent_plus_unescape (const char *string, int nulrepl)
{
return do_plus_or_plain_unescape (string, 1, nulrepl);
}
/* Create a new allocated string from STRING with all "%xx" sequences
decoded. Embedded Nul characters are replaced by the value of
NULREPL. The function returns the new string or NULL in case of a
malloc failure. */
char *
percent_unescape (const char *string, int nulrepl)
{
return do_plus_or_plain_unescape (string, 0, nulrepl);
}
static size_t
do_unescape_inplace (char *string, int withplus, int nulrepl)
{
unsigned char *p, *p0;
p = p0 = string;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
*p = xtoi_2 (string);
if (!*p)
*p = nulrepl;
string++;
}
else if (*string == '+' && withplus)
*p = ' ';
else
*p = *string;
p++;
string++;
}
return (p - p0);
}
/* Perform percent and plus unescaping in STRING and return the new
valid length of the string. Embedded Nul characters are replaced
by the value of NULREPL. A terminating Nul character is not
inserted; the caller might want to call this function this way:
foo[percent_plus_unescape_inplace (foo, 0)] = 0;
*/
size_t
percent_plus_unescape_inplace (char *string, int nulrepl)
{
return do_unescape_inplace (string, 1, nulrepl);
}
/* Perform percent unescaping in STRING and return the new valid
length of the string. Embedded Nul characters are replaced by the
value of NULREPL. A terminating Nul character is not inserted; the
caller might want to call this function this way:
foo[percent_unescape_inplace (foo, 0)] = 0;
*/
size_t
percent_unescape_inplace (char *string, int nulrepl)
{
return do_unescape_inplace (string, 0, nulrepl);
}

View File

@ -471,7 +471,9 @@ gnupg_reopen_std (const char *pgmname)
if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2)
exit (3);
#endif /* HAVE_STAT && !HAVE_W32_SYSTEM */
#else /* !(HAVE_STAT && !HAVE_W32_SYSTEM) */
(void)pgmname;
#endif
}
@ -479,11 +481,11 @@ gnupg_reopen_std (const char *pgmname)
void
gnupg_allow_set_foregound_window (pid_t pid)
{
if (!pid || pid == (pid_t)(-1))
if (!pid)
log_info ("%s called with invalid pid %lu\n",
"gnupg_allow_set_foregound_window", (unsigned long)pid);
#ifdef HAVE_W32_SYSTEM
else if (!AllowSetForegroundWindow (pid))
else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid))
log_info ("AllowSetForegroundWindow(%lu) failed: %s\n",
(unsigned long)pid, w32_strerror (-1));
#endif

View File

@ -66,8 +66,9 @@ test_percent_plus_escape (void)
"%0A+ABC%09"
}, { NULL, NULL }
};
char *buf;
char *buf, *buf2;
int i;
size_t len;
for (i=0; tbl[i].string; i++)
{
@ -79,17 +80,34 @@ test_percent_plus_escape (void)
}
if (strcmp (buf, tbl[i].expect))
fail (i);
buf2 = percent_plus_unescape (buf, 0);
if (!buf2)
{
fprintf (stderr, "out of core: %s\n", strerror (errno));
exit (2);
}
if (strcmp (buf2, tbl[i].string))
fail (i);
xfree (buf2);
/* Now test the inplace conversion. */
len = percent_plus_unescape_inplace (buf, 0);
buf[len] = 0;
if (strcmp (buf, tbl[i].string))
fail (i);
xfree (buf);
}
}
int
main (int argc, char **argv)
{
(void)argc;
(void)argv;
/* FIXME: We escape_unescape is not tested - only
percent_plus_unescape. */
test_percent_plus_escape ();
return 0;

View File

@ -204,6 +204,11 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count);
/*-- percent.c --*/
char *percent_plus_escape (const char *string);
char *percent_plus_unescape (const char *string, int nulrepl);
char *percent_unescape (const char *string, int nulrepl);
size_t percent_plus_unescape_inplace (char *string, int nulrepl);
size_t percent_unescape_inplace (char *string, int nulrepl);
/*-- homedir.c --*/

View File

@ -1,3 +1,11 @@
2009-03-31 Werner Koch <wk@g10code.com>
* passphrase.c (ask_passphrase): Use percent_plus_unescape.
* misc.c (unescape_percent_string): Remove.
* call-agent.c (unescape_status_string): Chnage to use
percent_plus_unescape.
2009-03-25 Werner Koch <wk@g10code.com>
* mainproc.c (print_pkenc_list): Use snprintf.

View File

@ -116,32 +116,7 @@ start_agent (void)
static char *
unescape_status_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xtrymalloc (strlen (s)+1);
if (!buffer)
return NULL;
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
return percent_plus_unescape (s, 0xff);
}

View File

@ -135,7 +135,6 @@ char *optsep(char **stringp);
char *argsplit(char *string);
int parse_options(char *str,unsigned int *options,
struct parse_options *opts,int noisy);
char *unescape_percent_string (const unsigned char *s);
int has_invalid_email_chars (const char *s);
int is_valid_mailbox (const char *name);
const char *get_libexecdir (void);

View File

@ -1215,39 +1215,6 @@ parse_options(char *str,unsigned int *options,
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary nul will
silently be replaced by a 0xFF. */
char *
unescape_percent_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xmalloc (strlen (s)+1);
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
}
/* Check whether the string has characters not valid in an RFC-822
address. To cope with OpenPGP we ignore allow non-ascii characters
so that for example umlauts are legal in an email address. An

View File

@ -453,8 +453,9 @@ ask_passphrase (const char *description,
{
if (strchr (description, '%'))
{
char *tmp = unescape_percent_string
((const unsigned char*)description);
char *tmp = percent_plus_unescape (description, 0xff);
if (!tmp)
log_fatal(_("out of core\n"));
tty_printf ("\n%s\n", tmp);
xfree (tmp);
}

View File

@ -1,3 +1,8 @@
2009-03-31 Werner Koch <wk@g10code.com>
* command.c (percent_plus_unescape): Remove.
(cmd_setattr): Use percent_plus_unescape_inplace.
2009-03-30 Werner Koch <wk@g10code.com>
* app-nks.c (do_decipher): Make it work for TCOS 3.

View File

@ -462,41 +462,6 @@ open_card (ctrl_t ctrl, const char *apptype)
}
/* Do the percent and plus/space unescaping in place and return the
length of the valid buffer. */
static size_t
percent_plus_unescape (unsigned char *string)
{
unsigned char *p = string;
size_t n = 0;
while (*string)
{
if (*string == '%' && string[1] && string[2])
{
string++;
*p++ = xtoi_2 (string);
n++;
string+= 2;
}
else if (*string == '+')
{
*p++ = ' ';
n++;
string++;
}
else
{
*p++ = *string++;
n++;
}
}
return n;
}
/* SERIALNO [APPTYPE]
Return the serial number of the card using a status reponse. This
@ -1153,7 +1118,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
*line++ = 0;
while (spacep (line))
line++;
nbytes = percent_plus_unescape ((unsigned char*)line);
nbytes = percent_plus_unescape_inplace (line, 0);
rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx,
(const unsigned char*)line, nbytes);

View File

@ -1,3 +1,9 @@
2009-04-01 Werner Koch <wk@g10code.com>
* export.c (popen_protect_tool): Add command line option
--agent-program and pass flag bit 6.
* import.c (popen_protect_tool): Ditto.
2009-03-26 Werner Koch <wk@g10code.com>
* gpgsm.c (main): s/def_digest_string/forced_digest_algo/ and

View File

@ -574,13 +574,14 @@ popen_protect_tool (ctrl_t ctrl, const char *pgmname,
const char *prompt, const char *keygrip,
pid_t *pid)
{
const char *argv[20];
const char *argv[22];
int i=0;
/* Make sure that the agent is running so that the protect tool is
able to ask for a passphrase. This has only an effect under W32
where the agent is started on demand; sending a NOP does not harm
on other platforms. */
on other platforms. This is not really necessary anymore because
the protect tool does this now by itself; it does not harm either.*/
gpgsm_agent_send_nop (ctrl);
argv[i++] = "--homedir";
@ -595,13 +596,18 @@ popen_protect_tool (ctrl_t ctrl, const char *pgmname,
argv[i++] = "--p12-charset";
argv[i++] = opt.p12_charset;
}
if (opt.agent_program)
{
argv[i++] = "--agent-program";
argv[i++] = opt.agent_program;
}
argv[i++] = "--",
argv[i++] = keygrip,
argv[i] = NULL;
assert (i < sizeof argv);
return gnupg_spawn_process (pgmname, argv, infile, outfile,
setup_pinentry_env, 128,
setup_pinentry_env, (128|64),
statusfile, pid);
}

View File

@ -463,13 +463,14 @@ static gpg_error_t
popen_protect_tool (ctrl_t ctrl, const char *pgmname,
FILE *infile, FILE *outfile, FILE **statusfile, pid_t *pid)
{
const char *argv[20];
const char *argv[22];
int i=0;
/* Make sure that the agent is running so that the protect tool is
able to ask for a passphrase. This has only an effect under W32
where the agent is started on demand; sending a NOP does not harm
on other platforms. */
on other platforms. This is not really necessary anymore because
the protect tool does this now by itself; it does not harm either. */
gpgsm_agent_send_nop (ctrl);
argv[i++] = "--homedir";
@ -483,12 +484,17 @@ popen_protect_tool (ctrl_t ctrl, const char *pgmname,
argv[i++] = "--passphrase";
argv[i++] = opt.fixed_passphrase;
}
if (opt.agent_program)
{
argv[i++] = "--agent-program";
argv[i++] = opt.agent_program;
}
argv[i++] = "--",
argv[i] = NULL;
assert (i < sizeof argv);
return gnupg_spawn_process (pgmname, argv, infile, outfile,
setup_pinentry_env, 128,
setup_pinentry_env, (128 | 64),
statusfile, pid);
}

View File

@ -215,7 +215,7 @@ gnu_getcwd (void)
}
/* Unescale STRING and returned the malloced result. The surrounding
/* Unescape STRING and returned the malloced result. The surrounding
quotes must already be removed from STRING. */
static char *
unescape_string (const char *string)

View File

@ -1141,7 +1141,7 @@ my_dgettext (const char *domain, const char *msgid)
/* FIXME: we have no dgettext, thus we can't switch. */
text = gettext (msgid);
text = (char*)gettext (msgid);
return text ? text : msgid;
}
#elif defined(ENABLE_NLS)