mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-17 14:07:03 +01:00
Avoid using the protect-tool to import pkcs#12.
This commit is contained in:
parent
28b3b74cbb
commit
006fd75aea
@ -1,3 +1,29 @@
|
||||
2010-06-15 Werner Koch <wk@g10code.com>
|
||||
|
||||
* command.c (cmd_keywrap_key, cmd_import_key): New.
|
||||
|
||||
* genkey.c (agent_genkey, agent_protect_and_store): Factor common
|
||||
code out to...
|
||||
(agent_ask_new_passphrase): .. new.
|
||||
|
||||
* findkey.c (agent_write_private_key): Return GPG_ERR_EEXIST
|
||||
instead of GPG_ERR_GENERAL.
|
||||
|
||||
2010-06-14 Werner Koch <wk@g10code.com>
|
||||
|
||||
* protect-tool.c: Remove commands --p12-import and --p12-export.
|
||||
* minip12.c, minip12.h: Move to ../sm.
|
||||
* Makefile.am (gpg_protect_tool_SOURCES): Remove them.
|
||||
* preset-passphrase.c: Remove unneeded minip12.h.
|
||||
|
||||
* command.c (cmd_keywrap_key): New.
|
||||
|
||||
* command.c (leave_cmd): New.
|
||||
(cmd_istrusted, cmd_listtrusted, cmd_marktrusted, cmd_pksign)
|
||||
(cmd_pkdecrypt, cmd_genkey, cmd_readkey, cmd_keyinfo)
|
||||
(cmd_get_passphrase, cmd_get_confirmation, cmd_learn)
|
||||
(cmd_passwd, cmd_preset_passphrase, cmd_getval, cmd_putval): Use it.
|
||||
|
||||
2010-05-12 Werner Koch <wk@g10code.com>
|
||||
|
||||
* preset-passphrase.c (forget_passphrase): Actually implement
|
||||
|
@ -79,8 +79,7 @@ gpg_agent_DEPENDENCIES = $(gpg_agent_res_deps)
|
||||
|
||||
gpg_protect_tool_SOURCES = \
|
||||
protect-tool.c \
|
||||
protect.c \
|
||||
minip12.c minip12.h
|
||||
protect.c
|
||||
|
||||
gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS)
|
||||
gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
|
||||
|
@ -284,6 +284,8 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
|
||||
|
||||
/*-- genkey.c --*/
|
||||
int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
|
||||
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
||||
char **r_passphrase);
|
||||
int agent_genkey (ctrl_t ctrl,
|
||||
const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
|
||||
int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
|
||||
|
278
agent/command.c
278
agent/command.c
@ -38,10 +38,14 @@
|
||||
#include <assuan.h>
|
||||
#include "i18n.h"
|
||||
|
||||
/* maximum allowed size of the inquired ciphertext */
|
||||
/* Maximum allowed size of the inquired ciphertext. */
|
||||
#define MAXLEN_CIPHERTEXT 4096
|
||||
/* maximum allowed size of the key parameters */
|
||||
/* Maximum allowed size of the key parameters. */
|
||||
#define MAXLEN_KEYPARAM 1024
|
||||
/* Maximum allowed size of key data as used in inquiries (bytes). */
|
||||
#define MAXLEN_KEYDATA 4096
|
||||
/* The size of the import/export KEK key (in bytes). */
|
||||
#define KEYWRAP_KEYSIZE (128/8)
|
||||
|
||||
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
|
||||
|
||||
@ -63,6 +67,8 @@ struct server_local_s
|
||||
the end of this session. */
|
||||
int allow_pinentry_notify; /* Set if pinentry notifications should
|
||||
be done. */
|
||||
void *import_key; /* Malloced KEK for the import_key command. */
|
||||
void *export_key; /* Malloced KEK for the export_key command. */
|
||||
};
|
||||
|
||||
|
||||
@ -340,6 +346,26 @@ agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid)
|
||||
}
|
||||
|
||||
|
||||
/* Helper to print a message while leaving a command. */
|
||||
static gpg_error_t
|
||||
leave_cmd (assuan_context_t ctx, gpg_error_t err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
const char *name = assuan_get_command_name (ctx);
|
||||
if (!name)
|
||||
name = "?";
|
||||
if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
|
||||
log_error ("command '%s' failed: %s\n", name,
|
||||
gpg_strerror (err));
|
||||
else
|
||||
log_error ("command '%s' failed: %s <%s>\n", name,
|
||||
gpg_strerror (err), gpg_strsource (err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_geteventcounter[] =
|
||||
"GETEVENTCOUNTER\n"
|
||||
@ -432,10 +458,7 @@ cmd_istrusted (assuan_context_t ctx, char *line)
|
||||
else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF )
|
||||
return gpg_error (GPG_ERR_NOT_TRUSTED);
|
||||
else
|
||||
{
|
||||
log_error ("command is_trusted failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -451,9 +474,7 @@ cmd_listtrusted (assuan_context_t ctx, char *line)
|
||||
(void)line;
|
||||
|
||||
rc = agent_listtrusted (ctx);
|
||||
if (rc)
|
||||
log_error ("command listtrusted failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -494,9 +515,7 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
|
||||
p++;
|
||||
|
||||
rc = agent_marktrusted (ctrl, p, fpr, flag);
|
||||
if (rc)
|
||||
log_error ("command marktrusted failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -695,11 +714,9 @@ cmd_pksign (assuan_context_t ctx, char *line)
|
||||
clear_outbuf (&outbuf);
|
||||
else
|
||||
rc = write_and_clear_outbuf (ctx, &outbuf);
|
||||
if (rc)
|
||||
log_error ("command pksign failed: %s\n", gpg_strerror (rc));
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
ctrl->server_local->keydesc = NULL;
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -734,11 +751,9 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
|
||||
clear_outbuf (&outbuf);
|
||||
else
|
||||
rc = write_and_clear_outbuf (ctx, &outbuf);
|
||||
if (rc)
|
||||
log_error ("command pkdecrypt failed: %s\n", gpg_strerror (rc));
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
ctrl->server_local->keydesc = NULL;
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -780,9 +795,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
|
||||
clear_outbuf (&outbuf);
|
||||
else
|
||||
rc = write_and_clear_outbuf (ctx, &outbuf);
|
||||
if (rc)
|
||||
log_error ("command genkey failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -825,9 +838,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
|
||||
gcry_sexp_release (s_pkey);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
log_error ("command readkey failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -967,7 +978,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
|
||||
if (dir)
|
||||
closedir (dir);
|
||||
if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
|
||||
log_error ("command keyinfo failed: %s\n", gpg_strerror (err));
|
||||
leave_cmd (ctx, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1167,9 +1178,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
|
||||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
log_error ("command get_passphrase failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1240,9 +1249,7 @@ cmd_get_confirmation (assuan_context_t ctx, char *line)
|
||||
plus_to_blank (desc);
|
||||
|
||||
rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
|
||||
if (rc)
|
||||
log_error ("command get_confirmation failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1259,9 +1266,7 @@ cmd_learn (assuan_context_t ctx, char *line)
|
||||
int rc;
|
||||
|
||||
rc = agent_handle_learn (ctrl, has_option (line, "--send")? ctx : NULL);
|
||||
if (rc)
|
||||
log_error ("command learn failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1304,9 +1309,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
||||
leave:
|
||||
gcry_sexp_release (s_skey);
|
||||
xfree (shadow_info);
|
||||
if (rc)
|
||||
log_error ("command passwd failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1371,10 +1374,7 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
|
||||
if (!rc)
|
||||
rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl);
|
||||
|
||||
if (rc)
|
||||
log_error ("command preset_passphrase failed: %s\n", gpg_strerror (rc));
|
||||
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1396,6 +1396,186 @@ cmd_scd (assuan_context_t ctx, char *line)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_keywrap_key[] =
|
||||
"KEYWRAP_KEY [--clear] <mode>\n"
|
||||
"\n"
|
||||
"Return a key to wrap another key. For now the key is returned\n"
|
||||
"verbatim and and thus makes not much sense because an eavesdropper on\n"
|
||||
"the gpg-agent connection will see the key as well as the wrapped key.\n"
|
||||
"However, this function may either be equipped with a public key\n"
|
||||
"mechanism or not used at all if the key is a pre-shared key. In any\n"
|
||||
"case wrapping the import and export of keys is a requirement for\n"
|
||||
"certain cryptographic validations and thus useful. The key persists\n"
|
||||
"a RESET command but may be cleared using the option --clear.\n"
|
||||
"\n"
|
||||
"Supported modes are:\n"
|
||||
" --import - Return a key to import a key into gpg-agent\n"
|
||||
" --export - Return a key to export a key from gpg-agent";
|
||||
static gpg_error_t
|
||||
cmd_keywrap_key (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err = 0;
|
||||
int clearopt = has_option (line, "--clear");
|
||||
|
||||
|
||||
assuan_begin_confidential (ctx);
|
||||
if (has_option (line, "--import"))
|
||||
{
|
||||
xfree (ctrl->server_local->import_key);
|
||||
if (clearopt)
|
||||
ctrl->server_local->import_key = NULL;
|
||||
else if (!(ctrl->server_local->import_key =
|
||||
gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM)))
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
err = assuan_send_data (ctx, ctrl->server_local->import_key,
|
||||
KEYWRAP_KEYSIZE);
|
||||
}
|
||||
else if (has_option (line, "--export"))
|
||||
{
|
||||
xfree (ctrl->server_local->export_key);
|
||||
if (clearopt)
|
||||
ctrl->server_local->export_key = NULL;
|
||||
else if (!(ctrl->server_local->export_key =
|
||||
gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM)))
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
err = assuan_send_data (ctx, ctrl->server_local->export_key,
|
||||
KEYWRAP_KEYSIZE);
|
||||
}
|
||||
else
|
||||
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for MODE");
|
||||
assuan_end_confidential (ctx);
|
||||
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_import_key[] =
|
||||
"IMPORT_KEY\n"
|
||||
"\n"
|
||||
"Import a secret key into the key store. The key is expected to be\n"
|
||||
"encrypted using the current session's key wrapping key (cf. command\n"
|
||||
"KEYWRAP_KEY) using the AESWRAP-128 algorithm. This function takes\n"
|
||||
"no arguments but uses the inquiry \"KEYDATA\" to ask for the actual\n"
|
||||
"key data. The unwrapped key must be a canonical S-expression.";
|
||||
static gpg_error_t
|
||||
cmd_import_key (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
unsigned char *wrappedkey = NULL;
|
||||
size_t wrappedkeylen;
|
||||
gcry_cipher_hd_t cipherhd = NULL;
|
||||
unsigned char *key = NULL;
|
||||
size_t keylen, realkeylen;
|
||||
char *passphrase = NULL;
|
||||
unsigned char *finalkey = NULL;
|
||||
size_t finalkeylen;
|
||||
unsigned char grip[20];
|
||||
|
||||
(void)line;
|
||||
|
||||
if (!ctrl->server_local->import_key)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_BAD_KEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
assuan_begin_confidential (ctx);
|
||||
err = assuan_inquire (ctx, "KEYDATA",
|
||||
&wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA);
|
||||
assuan_end_confidential (ctx);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (wrappedkeylen < 24)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_LENGTH);
|
||||
goto leave;
|
||||
}
|
||||
keylen = wrappedkeylen - 8;
|
||||
key = xtrymalloc_secure (keylen);
|
||||
if (!key)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
|
||||
GCRY_CIPHER_MODE_AESWRAP, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = gcry_cipher_setkey (cipherhd,
|
||||
ctrl->server_local->import_key, KEYWRAP_KEYSIZE);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen);
|
||||
if (err)
|
||||
goto leave;
|
||||
gcry_cipher_close (cipherhd);
|
||||
cipherhd = NULL;
|
||||
xfree (wrappedkey);
|
||||
wrappedkey = NULL;
|
||||
|
||||
realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
|
||||
if (!realkeylen)
|
||||
goto leave; /* Invalid canonical encoded S-expression. */
|
||||
|
||||
err = keygrip_from_canon_sexp (key, realkeylen, grip);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
if (!agent_key_available (grip))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_EEXIST);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = agent_ask_new_passphrase
|
||||
(ctrl, _("Please enter the passphrase to protect the "
|
||||
"imported object within the GnuPG system."),
|
||||
&passphrase);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
if (passphrase)
|
||||
{
|
||||
err = agent_protect (key, passphrase, &finalkey, &finalkeylen);
|
||||
if (!err)
|
||||
err = agent_write_private_key (grip, finalkey, finalkeylen, 0);
|
||||
}
|
||||
else
|
||||
err = agent_write_private_key (grip, key, realkeylen, 0);
|
||||
|
||||
leave:
|
||||
xfree (finalkey);
|
||||
xfree (passphrase);
|
||||
xfree (key);
|
||||
gcry_cipher_close (cipherhd);
|
||||
xfree (wrappedkey);
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_export_key[] =
|
||||
"EXPORT_KEY\n"
|
||||
"\n";
|
||||
static gpg_error_t
|
||||
cmd_export_key (assuan_context_t ctx, char *line)
|
||||
{
|
||||
gpg_error_t err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||
|
||||
|
||||
/* leave: */
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static const char hlp_getval[] =
|
||||
"GETVAL <key>\n"
|
||||
@ -1435,9 +1615,7 @@ cmd_getval (assuan_context_t ctx, char *line)
|
||||
else
|
||||
return gpg_error (GPG_ERR_NO_DATA);
|
||||
|
||||
if (rc)
|
||||
log_error ("command getval failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1520,9 +1698,7 @@ cmd_putval (assuan_context_t ctx, char *line)
|
||||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
log_error ("command putval failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
return leave_cmd (ctx, rc);
|
||||
}
|
||||
|
||||
|
||||
@ -1641,7 +1817,7 @@ static const char hlp_getinfo[] =
|
||||
" std_session_env - List the standard session environment.\n"
|
||||
" std_startup_env - List the standard startup environment.\n"
|
||||
" cmd_has_option\n"
|
||||
" - Returns OK if the command CMD implements the option OPT.";
|
||||
" - Returns OK if the command CMD implements the option OPT\n.";
|
||||
static gpg_error_t
|
||||
cmd_getinfo (assuan_context_t ctx, char *line)
|
||||
{
|
||||
@ -1910,6 +2086,9 @@ register_commands (assuan_context_t ctx)
|
||||
{ "INPUT", NULL },
|
||||
{ "OUTPUT", NULL },
|
||||
{ "SCD", cmd_scd, hlp_scd },
|
||||
{ "KEYWRAP_KEY", cmd_keywrap_key, hlp_keywrap_key },
|
||||
{ "IMPORT_KEY", cmd_import_key, hlp_import_key },
|
||||
{ "EXPORT_KEY", cmd_export_key, hlp_export_key },
|
||||
{ "GETVAL", cmd_getval, hlp_getval },
|
||||
{ "PUTVAL", cmd_putval, hlp_putval },
|
||||
{ "UPDATESTARTUPTTY", cmd_updatestartuptty, hlp_updatestartuptty },
|
||||
@ -2021,6 +2200,9 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
|
||||
|
||||
/* Cleanup. */
|
||||
assuan_release (ctx);
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
xfree (ctrl->server_local->import_key);
|
||||
xfree (ctrl->server_local->export_key);
|
||||
if (ctrl->server_local->stopme)
|
||||
agent_exit (0);
|
||||
xfree (ctrl->server_local);
|
||||
|
@ -69,7 +69,7 @@ agent_write_private_key (const unsigned char *grip,
|
||||
{
|
||||
log_error ("secret key file `%s' already exists\n", fname);
|
||||
xfree (fname);
|
||||
return gpg_error (GPG_ERR_GENERAL);
|
||||
return gpg_error (GPG_ERR_EEXIST);
|
||||
}
|
||||
|
||||
/* FIXME: On POSIX systems we used include S_IRGRP as well. */
|
||||
@ -883,8 +883,8 @@ agent_public_key_from_file (ctrl_t ctrl,
|
||||
|
||||
|
||||
|
||||
/* Return the secret key as an S-Exp after locating it using the grip.
|
||||
Returns NULL if key is not available. 0 = key is available */
|
||||
/* Check whether the the secret key identified by GRIP is available.
|
||||
Returns 0 is the key is available. */
|
||||
int
|
||||
agent_key_available (const unsigned char *grip)
|
||||
{
|
||||
|
199
agent/genkey.c
199
agent/genkey.c
@ -1,5 +1,5 @@
|
||||
/* genkey.c - Generate a keypair
|
||||
* Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2003, 2004, 2007, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -286,6 +286,69 @@ reenter_compare_cb (struct pin_entry_info_s *pi)
|
||||
}
|
||||
|
||||
|
||||
/* Ask the user for a new passphrase using PROMPT. On success the
|
||||
function returns 0 and store the passphrase at R_PASSPHRASE; if the
|
||||
user opted not to use a passphrase NULL will be stored there. The
|
||||
user needs to free the returned string. In case of an error and
|
||||
error code is returned and NULL stored at R_PASSPHRASE. */
|
||||
gpg_error_t
|
||||
agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
|
||||
char **r_passphrase)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *text1 = prompt;
|
||||
const char *text2 = _("Please re-enter this passphrase");
|
||||
const char *initial_errtext = NULL;
|
||||
struct pin_entry_info_s *pi, *pi2;
|
||||
|
||||
*r_passphrase = NULL;
|
||||
|
||||
pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
|
||||
pi2 = pi + (sizeof *pi + 100);
|
||||
pi->max_length = 100;
|
||||
pi->max_tries = 3;
|
||||
pi->with_qualitybar = 1;
|
||||
pi2->max_length = 100;
|
||||
pi2->max_tries = 3;
|
||||
pi2->check_cb = reenter_compare_cb;
|
||||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
next_try:
|
||||
err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
|
||||
initial_errtext = NULL;
|
||||
if (!err)
|
||||
{
|
||||
if (check_passphrase_constraints (ctrl, pi->pin, 0))
|
||||
{
|
||||
pi->failed_tries = 0;
|
||||
pi2->failed_tries = 0;
|
||||
goto next_try;
|
||||
}
|
||||
/* Unless the passphrase is empty, ask to confirm it. */
|
||||
if (pi->pin && *pi->pin)
|
||||
{
|
||||
err = agent_askpin (ctrl, text2, NULL, NULL, pi2);
|
||||
if (err == -1)
|
||||
{ /* The re-entered one did not match and the user did not
|
||||
hit cancel. */
|
||||
initial_errtext = _("does not match - try again");
|
||||
goto next_try;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && *pi->pin)
|
||||
{
|
||||
/* User wants a passphrase. */
|
||||
*r_passphrase = xtrystrdup (pi->pin);
|
||||
if (!*r_passphrase)
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
xfree (pi);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Generate a new keypair according to the parameters given in
|
||||
KEYPARAM */
|
||||
@ -294,7 +357,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
membuf_t *outbuf)
|
||||
{
|
||||
gcry_sexp_t s_keyparam, s_key, s_private, s_public;
|
||||
struct pin_entry_info_s *pi, *pi2;
|
||||
char *passphrase = NULL;
|
||||
int rc;
|
||||
size_t len;
|
||||
char *buf;
|
||||
@ -307,63 +370,19 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
}
|
||||
|
||||
/* Get the passphrase now, cause key generation may take a while. */
|
||||
{
|
||||
const char *text1 = _("Please enter the passphrase to%0A"
|
||||
"to protect your new key");
|
||||
const char *text2 = _("Please re-enter this passphrase");
|
||||
const char *initial_errtext = NULL;
|
||||
|
||||
pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
|
||||
pi2 = pi + (sizeof *pi + 100);
|
||||
pi->max_length = 100;
|
||||
pi->max_tries = 3;
|
||||
pi->with_qualitybar = 1;
|
||||
pi2->max_length = 100;
|
||||
pi2->max_tries = 3;
|
||||
pi2->check_cb = reenter_compare_cb;
|
||||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
next_try:
|
||||
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
|
||||
initial_errtext = NULL;
|
||||
if (!rc)
|
||||
{
|
||||
if (check_passphrase_constraints (ctrl, pi->pin, 0))
|
||||
{
|
||||
pi->failed_tries = 0;
|
||||
pi2->failed_tries = 0;
|
||||
goto next_try;
|
||||
}
|
||||
if (pi->pin && *pi->pin)
|
||||
{
|
||||
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
|
||||
if (rc == -1)
|
||||
{ /* The re-entered one did not match and the user did not
|
||||
hit cancel. */
|
||||
initial_errtext = _("does not match - try again");
|
||||
goto next_try;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!*pi->pin)
|
||||
{
|
||||
xfree (pi);
|
||||
pi = NULL; /* User does not want a passphrase. */
|
||||
}
|
||||
}
|
||||
rc = agent_ask_new_passphrase (ctrl,
|
||||
_("Please enter the passphrase to%0A"
|
||||
"to protect your new key"),
|
||||
&passphrase);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = gcry_pk_genkey (&s_key, s_keyparam );
|
||||
gcry_sexp_release (s_keyparam);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("key generation failed: %s\n", gpg_strerror (rc));
|
||||
xfree (pi);
|
||||
xfree (passphrase);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -373,7 +392,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
{
|
||||
log_error ("key generation failed: invalid return value\n");
|
||||
gcry_sexp_release (s_key);
|
||||
xfree (pi);
|
||||
xfree (passphrase);
|
||||
return gpg_error (GPG_ERR_INV_DATA);
|
||||
}
|
||||
s_public = gcry_sexp_find_token (s_key, "public-key", 0);
|
||||
@ -382,7 +401,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
log_error ("key generation failed: invalid return value\n");
|
||||
gcry_sexp_release (s_private);
|
||||
gcry_sexp_release (s_key);
|
||||
xfree (pi);
|
||||
xfree (passphrase);
|
||||
return gpg_error (GPG_ERR_INV_DATA);
|
||||
}
|
||||
gcry_sexp_release (s_key); s_key = NULL;
|
||||
@ -390,8 +409,9 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
/* store the secret key */
|
||||
if (DBG_CRYPTO)
|
||||
log_debug ("storing private key\n");
|
||||
rc = store_key (s_private, pi? pi->pin:NULL, 0);
|
||||
xfree (pi); pi = NULL;
|
||||
rc = store_key (s_private, passphrase, 0);
|
||||
xfree (passphrase);
|
||||
passphrase = NULL;
|
||||
gcry_sexp_release (s_private);
|
||||
if (rc)
|
||||
{
|
||||
@ -423,65 +443,20 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
|
||||
|
||||
|
||||
|
||||
/* Apply a new passpahrse to the key S_SKEY and store it. */
|
||||
/* Apply a new passphrase to the key S_SKEY and store it. */
|
||||
int
|
||||
agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
|
||||
{
|
||||
struct pin_entry_info_s *pi, *pi2;
|
||||
int rc;
|
||||
char *passphrase;
|
||||
|
||||
{
|
||||
const char *text1 = _("Please enter the new passphrase");
|
||||
const char *text2 = _("Please re-enter this passphrase");
|
||||
const char *initial_errtext = NULL;
|
||||
|
||||
pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
|
||||
pi2 = pi + (sizeof *pi + 100);
|
||||
pi->max_length = 100;
|
||||
pi->max_tries = 3;
|
||||
pi->with_qualitybar = 1;
|
||||
pi2->max_length = 100;
|
||||
pi2->max_tries = 3;
|
||||
pi2->check_cb = reenter_compare_cb;
|
||||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
next_try:
|
||||
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
|
||||
initial_errtext = NULL;
|
||||
if (!rc)
|
||||
{
|
||||
if (check_passphrase_constraints (ctrl, pi->pin, 0))
|
||||
{
|
||||
pi->failed_tries = 0;
|
||||
pi2->failed_tries = 0;
|
||||
goto next_try;
|
||||
}
|
||||
/* Unless the passphrase is empty, ask to confirm it. */
|
||||
if (pi->pin && *pi->pin)
|
||||
{
|
||||
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
|
||||
if (rc == -1)
|
||||
{ /* The re-entered one did not match and the user did not
|
||||
hit cancel. */
|
||||
initial_errtext = _("does not match - try again");
|
||||
goto next_try;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!*pi->pin)
|
||||
{
|
||||
xfree (pi);
|
||||
pi = NULL; /* User does not want a passphrase. */
|
||||
}
|
||||
}
|
||||
|
||||
rc = store_key (s_skey, pi? pi->pin:NULL, 1);
|
||||
xfree (pi);
|
||||
rc = agent_ask_new_passphrase (ctrl,
|
||||
_("Please enter the new passphrase"),
|
||||
&passphrase);
|
||||
if (!rc)
|
||||
{
|
||||
rc = store_key (s_skey, passphrase, 1);
|
||||
xfree (passphrase);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -43,7 +43,6 @@
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "agent.h"
|
||||
#include "minip12.h"
|
||||
#include "simple-pwquery.h"
|
||||
#include "i18n.h"
|
||||
#include "sysutils.h"
|
||||
|
@ -40,7 +40,6 @@
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "agent.h"
|
||||
#include "minip12.h"
|
||||
#include "i18n.h"
|
||||
#include "get-passphrase.h"
|
||||
#include "sysutils.h"
|
||||
@ -63,8 +62,6 @@ enum cmd_and_opt_values
|
||||
oS2Kcalibration,
|
||||
oCanonical,
|
||||
|
||||
oP12Import,
|
||||
oP12Export,
|
||||
oP12Charset,
|
||||
oStore,
|
||||
oForce,
|
||||
@ -116,11 +113,6 @@ static ARGPARSE_OPTS opts[] = {
|
||||
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"),
|
||||
|
||||
ARGPARSE_c (oS2Kcalibration, "s2k-calibration", "@"),
|
||||
|
||||
ARGPARSE_group (301, N_("@\nOptions:\n ")),
|
||||
@ -635,7 +627,7 @@ rsa_key_check (struct rsa_secret_key_s *skey)
|
||||
return err? -1:0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* A callback used by p12_parse to return a certificate. */
|
||||
static void
|
||||
import_p12_cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
|
||||
@ -793,6 +785,7 @@ import_p12_file (const char *fname)
|
||||
|
||||
xfree (result);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -865,6 +858,7 @@ is_keygrip (const char *string)
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void
|
||||
export_p12_file (const char *fname)
|
||||
{
|
||||
@ -1009,6 +1003,7 @@ export_p12_file (const char *fname)
|
||||
fwrite (key, keylen, 1, stdout);
|
||||
xfree (key);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -1059,8 +1054,6 @@ main (int argc, char **argv )
|
||||
case oShadow: cmd = oShadow; break;
|
||||
case oShowShadowInfo: cmd = oShowShadowInfo; break;
|
||||
case oShowKeygrip: cmd = oShowKeygrip; break;
|
||||
case oP12Import: cmd = oP12Import; break;
|
||||
case oP12Export: cmd = oP12Export; break;
|
||||
case oP12Charset: opt_p12_charset = pargs.r.ret_str; break;
|
||||
|
||||
case oS2Kcalibration: cmd = oS2Kcalibration; break;
|
||||
@ -1105,10 +1098,6 @@ main (int argc, char **argv )
|
||||
show_shadow_info (fname);
|
||||
else if (cmd == oShowKeygrip)
|
||||
show_keygrip (fname);
|
||||
else if (cmd == oP12Import)
|
||||
import_p12_file (fname);
|
||||
else if (cmd == oP12Export)
|
||||
export_p12_file (fname);
|
||||
else if (cmd == oS2Kcalibration)
|
||||
{
|
||||
if (!opt.verbose)
|
||||
|
@ -415,7 +415,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
unsigned char *p;
|
||||
gcry_md_hd_t md;
|
||||
|
||||
/* Create an S-expression with the procted-at timestamp. */
|
||||
/* Create an S-expression with the protected-at timestamp. */
|
||||
memcpy (timestamp_exp, "(12:protected-at15:", 19);
|
||||
gnupg_get_isotime (timestamp_exp+19);
|
||||
timestamp_exp[19+15] = ')';
|
||||
|
@ -1,3 +1,11 @@
|
||||
2010-06-17 Werner Koch <wk@g10code.com>
|
||||
|
||||
* sexputil.c (make_canon_sexp_pad): New.
|
||||
|
||||
2010-06-14 Werner Koch <wk@g10code.com>
|
||||
|
||||
* membuf.c (put_membuf): Add shortcut for !LEN.
|
||||
|
||||
2010-06-11 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* sysutils.c (translate_sys2libc_fd): Revert last change.
|
||||
|
@ -59,7 +59,7 @@ init_membuf_secure (membuf_t *mb, int initiallen)
|
||||
void
|
||||
put_membuf (membuf_t *mb, const void *buf, size_t len)
|
||||
{
|
||||
if (mb->out_of_core)
|
||||
if (mb->out_of_core || !len)
|
||||
return;
|
||||
|
||||
if (mb->len + len >= mb->size)
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "sexp-parse.h"
|
||||
|
||||
|
||||
/* Helper function to create a a canonical encoded S-expression from a
|
||||
/* Helper function to create a canonical encoded S-expression from a
|
||||
Libgcrypt S-expression object. The function returns 0 on success
|
||||
and the malloced canonical S-expression is stored at R_BUFFER and
|
||||
the allocated length at R_BUFLEN. On error an error code is
|
||||
@ -71,6 +71,36 @@ make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen)
|
||||
}
|
||||
|
||||
|
||||
/* Same as make_canon_sexp but pad the buffer to multiple of 64
|
||||
bits. */
|
||||
gpg_error_t
|
||||
make_canon_sexp_pad (gcry_sexp_t sexp,
|
||||
unsigned char **r_buffer, size_t *r_buflen)
|
||||
{
|
||||
size_t len;
|
||||
unsigned char *buf;
|
||||
|
||||
*r_buffer = NULL;
|
||||
if (r_buflen)
|
||||
*r_buflen = 0;;
|
||||
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
if (!len)
|
||||
return gpg_error (GPG_ERR_BUG);
|
||||
len += (8 - len % 8) % 8;
|
||||
buf = xtrycalloc (1, len);
|
||||
if (!buf)
|
||||
return gpg_error_from_syserror ();
|
||||
if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len))
|
||||
return gpg_error (GPG_ERR_BUG);
|
||||
|
||||
*r_buffer = buf;
|
||||
if (r_buflen)
|
||||
*r_buflen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the so called "keygrip" which is the SHA-1 hash of the
|
||||
public key parameters expressed in a way depended on the algorithm.
|
||||
|
||||
|
@ -146,6 +146,8 @@ gpg_error_t b64dec_finish (struct b64state *state);
|
||||
/*-- sexputil.c */
|
||||
gpg_error_t make_canon_sexp (gcry_sexp_t sexp,
|
||||
unsigned char **r_buffer, size_t *r_buflen);
|
||||
gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp,
|
||||
unsigned char **r_buffer, size_t *r_buflen);
|
||||
gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
|
||||
unsigned char *grip);
|
||||
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
|
||||
|
@ -38,6 +38,8 @@
|
||||
an exit handler to cleanup the socket layer.
|
||||
*/
|
||||
|
||||
#warning Duplicated code with common/http.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
251
doc/dirmngr.texi
251
doc/dirmngr.texi
@ -786,3 +786,254 @@ as a binary blob.
|
||||
@end ifset
|
||||
@include see-also-note.texi
|
||||
|
||||
@c
|
||||
@c !!! UNDER CONSTRUCTION !!!
|
||||
@c
|
||||
@c
|
||||
@c @section Verifying a Certificate
|
||||
@c
|
||||
@c There are several ways to request services from Dirmngr. Almost all of
|
||||
@c them are done using the Assuan protocol. What we describe here is the
|
||||
@c Assuan command CHECKCRL as used for example by the dirmnr-client tool if
|
||||
@c invoked as
|
||||
@c
|
||||
@c @example
|
||||
@c dirmngr-client foo.crt
|
||||
@c @end example
|
||||
@c
|
||||
@c This command will send an Assuan request to an already running Dirmngr
|
||||
@c instance. foo.crt is expected to be a standard X.509 certificate and
|
||||
@c dirmngr will receive the Assuan command
|
||||
@c
|
||||
@c @example
|
||||
@c CHECKCRL @var [{fingerprint}]
|
||||
@c @end example
|
||||
@c
|
||||
@c @var{fingerprint} is optional and expected to be the SHA-1 has of the
|
||||
@c DER encoding of the certificate under question. It is to be HEX
|
||||
@c encoded. The rationale for sending the fingerprint is that it allows
|
||||
@c dirmngr to reply immediatly if it has already cached such a request. If
|
||||
@c this is not the case and no certificate has been found in dirmngr's
|
||||
@c internal certificate storage, dirmngr will request the certificate using
|
||||
@c the Assuan inquiry
|
||||
@c
|
||||
@c @example
|
||||
@c INQUIRE TARGETCERT
|
||||
@c @end example
|
||||
@c
|
||||
@c The caller (in our example dirmngr-client) is then expected to return
|
||||
@c the certificate for the request (which should match @var{fingerprint})
|
||||
@c as a binary blob.
|
||||
@c
|
||||
@c Dirmngr now passes control to @code{crl_cache_cert_isvalid}. This
|
||||
@c function checks whether a CRL item exists for target certificate. These
|
||||
@c CRL items are kept in a database of already loaded and verified CRLs.
|
||||
@c This mechanism is called the CRL cache. Obviously timestamps are kept
|
||||
@c there with each item to cope with the expiration date of the CRL. The
|
||||
@c possible return values are: @code{0} to indicate that a valid CRL is
|
||||
@c available for the certificate and the certificate itself is not listed
|
||||
@c in this CRL, @code{GPG_ERR_CERT_REVOKED} to indicate that the certificate is
|
||||
@c listed in the CRL or @code{GPG_ERR_NO_CRL_KNOWN} in cases where no CRL or no
|
||||
@c information is available. The first two codes are immediatly returned to
|
||||
@c the caller and the processing of this request has been done.
|
||||
@c
|
||||
@c Only the @code{GPG_ERR_NO_CRL_KNOWN} needs more attention: Dirmngr now
|
||||
@c calls @code{clr_cache_reload_crl} and if this succeeds calls
|
||||
@c @code{crl_cache_cert_isvald) once more. All further errors are
|
||||
@c immediately returned to the caller.
|
||||
@c
|
||||
@c @code{crl_cache_reload_crl} is the actual heart of the CRL management.
|
||||
@c It locates the corresponding CRL for the target certificate, reads and
|
||||
@c verifies this CRL and stores it in the CRL cache. It works like this:
|
||||
@c
|
||||
@c * Loop over all crlDPs in the target certificate.
|
||||
@c * If the crlDP is invalid immediately terminate the loop.
|
||||
@c * Loop over all names in the current crlDP.
|
||||
@c * If the URL scheme is unknown or not enabled
|
||||
@c (--ignore-http-dp, --ignore-ldap-dp) continues with
|
||||
@c the next name.
|
||||
@c * @code{crl_fetch} is called to actually retrieve the CRL.
|
||||
@c In case of problems this name is ignore and we continue with
|
||||
@c the next name. Note that @code{crl_fetch} does only return
|
||||
@c a descriptor for the CRL for further reading so does the CRL
|
||||
@c does not yet end up in memory.
|
||||
@c * @code{crl_cache_insert} is called with that descriptor to
|
||||
@c actually read the CRL into the cache. See below for a
|
||||
@c description of this function. If there is any error (e.g. read
|
||||
@c problem, CRL not correctly signed or verification of signature
|
||||
@c not possible), this descriptor is rejected and we continue
|
||||
@c with the next name. If the CRL has been successfully loaded,
|
||||
@c the loop is terminated.
|
||||
@c * If no crlDP has been found in the previous loop use a default CRL.
|
||||
@c Note, that if any crlDP has been found but loading of the CRL failed,
|
||||
@c this condition is not true.
|
||||
@c * Try to load a CRL from all configured servers (ldapservers.conf)
|
||||
@c in turn. The first server returning a CRL is used.
|
||||
@c * @code(crl_cache_insert) is then used to actually insert the CRL
|
||||
@c into the cache. If this failed we give up immediatley without
|
||||
@c checking the rest of the servers from the first step.
|
||||
@c * Ready.
|
||||
@c
|
||||
@c
|
||||
@c The @code{crl_cache_insert} function takes care of reading the bulk of
|
||||
@c the CRL, parsing it and checking the signature. It works like this: A
|
||||
@c new database file is created using a temporary file name. The CRL
|
||||
@c parsing machinery is started and all items of the CRL are put into
|
||||
@c this database file. At the end the issuer certificate of the CRL
|
||||
@c needs to be retrieved. Three cases are to be distinguished:
|
||||
@c
|
||||
@c a) An authorityKeyIdentifier with an issuer and serialno exits: The
|
||||
@c certificate is retrieved using @code{find_cert_bysn}. If
|
||||
@c the certificate is in the certificate cache, it is directly
|
||||
@c returned. Then the requester (i.e. the client who requested the
|
||||
@c CRL check) is asked via the Assuan inquiry ``SENDCERT'' whether
|
||||
@c he can provide this certificate. If this succeed the returned
|
||||
@c certificate gets cached and returned. Note, that dirmngr does not
|
||||
@c verify in any way whether the expected certificate is returned.
|
||||
@c It is in the interest of the client to return a useful certificate
|
||||
@c as otherwise the service request will fail due to a bad signature.
|
||||
@c The last way to get the certificate is by looking it up at
|
||||
@c external resources. This is done using the @code{ca_cert_fetch}
|
||||
@c and @code{fetch_next_ksba_cert} and comparing the returned
|
||||
@c certificate to match the requested issuer and seriano (This is
|
||||
@c needed because the LDAP layer may return several certificates as
|
||||
@c LDAP as no standard way to retrieve by serial number).
|
||||
@c
|
||||
@c b) An authorityKeyIdentifier with a key ID exists: The certificate is
|
||||
@c retrieved using @code{find_cert_bysubject}. If the certificate is
|
||||
@c in the certificate cache, it is directly returned. Then the
|
||||
@c requester is asked via the Assuan inquiry ``SENDCERT_SKI'' whether
|
||||
@c he can provide this certificate. If this succeed the returned
|
||||
@c certificate gets cached and returned. Note, that dirmngr does not
|
||||
@c verify in any way whether the expected certificate is returned.
|
||||
@c It is in the interest of the client to return a useful certificate
|
||||
@c as otherwise the service request will fail due to a bad signature.
|
||||
@c The last way to get the certificate is by looking it up at
|
||||
@c external resources. This is done using the @code{ca_cert_fetch}
|
||||
@c and @code{fetch_next_ksba_cert} and comparing the returned
|
||||
@c certificate to match the requested subject and key ID.
|
||||
@c
|
||||
@c c) No authorityKeyIdentifier exits: The certificate is retrieved
|
||||
@c using @code{find_cert_bysubject} without the key ID argument. If
|
||||
@c the certificate is in the certificate cache the first one with a
|
||||
@c matching subject is is directly returned. Then the requester is
|
||||
@c asked via the Assuan inquiry ``SENDCERT'' and an exact
|
||||
@c specification of the subject whether he can
|
||||
@c provide this certificate. If this succeed the returned
|
||||
@c certificate gets cached and returned. Note, that dirmngr does not
|
||||
@c verify in any way whether the expected certificate is returned.
|
||||
@c It is in the interest of the client to return a useful certificate
|
||||
@c as otherwise the service request will fail due to a bad signature.
|
||||
@c The last way to get the certificate is by looking it up at
|
||||
@c external resources. This is done using the @code{ca_cert_fetch}
|
||||
@c and @code{fetch_next_ksba_cert} and comparing the returned
|
||||
@c certificate to match the requested subject; the first certificate
|
||||
@c with a matching subject is then returned.
|
||||
@c
|
||||
@c If no certificate was found, the function returns with the error
|
||||
@c GPG_ERR_MISSING_CERT. Now the signature is verified. If this fails,
|
||||
@c the erro is returned. On success the @code{validate_cert_chain} is
|
||||
@c used to verify that the certificate is actually valid.
|
||||
@c
|
||||
@c Here we may encounter a recursive situation:
|
||||
@c @code{validate_cert_chain} needs to look at other certificates and
|
||||
@c also at CRLs to check whether tehse other certificates and well, the
|
||||
@c CRL issuer certificate itself are not revoked. FIXME: We need to make
|
||||
@c sure that @code{validate_cert_chain} does not try to lookup the CRL we
|
||||
@c are currently processing. This would be a catch-22 and may indicate a
|
||||
@c broken PKI. However, due to overlapping expiring times and imprecise
|
||||
@c clocks thsi may actually happen.
|
||||
@c
|
||||
@c For historical reasons the Assuan command ISVALID is a bit different
|
||||
@c to CHECKCRL but this is mainly due to different calling conventions.
|
||||
@c In the end the same fucntionality is used, albeit hidden by a couple
|
||||
@c of indirection and argument and result code mangling. It furthere
|
||||
@c ingetrages OCSP checking depending on options are the way it is
|
||||
@c called. GPGSM still uses this command but might eventuall switch over
|
||||
@c to CHECKCRL and CHECKOCSP so that ISVALID can be retired.
|
||||
@c
|
||||
@c
|
||||
@c @section Validating a certificate
|
||||
@c
|
||||
@c We describe here how the internal function @code{validate_cert_chain}
|
||||
@c works. Note that mainly testing purposes this functionality may be
|
||||
@c called directly using @cmd{dirmngr-client --validate @file{foo.crt}}.
|
||||
@c
|
||||
@c For backward compatibility this function returns success if Dirmngr is
|
||||
@c not used as a system daemon. Thus not validating the certicates at
|
||||
@c all. FIXME: This is definitely not correct and should be fixed ASAP.
|
||||
@c
|
||||
@c The function takes the target certificate and a mode argument as
|
||||
@c parameters and returns an error code and optionally the closes
|
||||
@c expiration time of all certificates in the chain.
|
||||
@c
|
||||
@c We first check that the certificate may be used for the requested
|
||||
@c purpose (i.e. OCSP or CRL signing). If this is not the case
|
||||
@c GPG_ERR_WRONG_KEY_USAGE is returned.
|
||||
@c
|
||||
@c The next step is to find the trust anchor (root certificate) and to
|
||||
@c assemble the chain in memory: Starting with the target certificate,
|
||||
@c the expiration time is checked against the current date, unknown
|
||||
@c critical extensions are detected and certificate policies are matched
|
||||
@c (We only allow 2.289.9.9 but I have no clue about that OID and from
|
||||
@c where I got it - it does not even seem to be assigned - debug cruft?).
|
||||
@c
|
||||
@c Now if this certificate is a self-signed one, we have reached the
|
||||
@c trust anchor. In this case we check that the signature is good, the
|
||||
@c certificate is allowed to act as a CA, that it is a trusted one (by
|
||||
@c checking whether it is has been put into the trusted-certs
|
||||
@c configuration directory) and finally prepend into to our list
|
||||
@c representing the certificate chain. This steps ends then.
|
||||
@c
|
||||
@c If it is not a self-signed certificate, we check that the chain won't
|
||||
@c get too long (current limit is 100), if this is the case we terminate
|
||||
@c with the error GPG_ERR_BAD_CERT_CHAIN.
|
||||
@c
|
||||
@c Now the issuer's certificate is looked up: If an
|
||||
@c authorityKeyIdentifier is available, this one is used to locate the
|
||||
@c certificate either using issuer and serialnumber or subject DN
|
||||
@c (i.e. the issuer's DN) and the keyID. The functions
|
||||
@c @code{find_cert_bysn) and @code{find_cert_bysubject} are used
|
||||
@c respectively. The have already been described above under the
|
||||
@c description of @code{crl_cache_insert}. If no certificate was found
|
||||
@c or with no authorityKeyIdentifier, only the cache is consulted using
|
||||
@c @code{get_cert_bysubject}. The latter is is done under the assumption
|
||||
@c that a matching certificate has explicitly been put into the
|
||||
@c certificate cache. If the issuer's certificate could not be found,
|
||||
@c the validation terminates with the error code @code{GPG_ERR_MISSING_CERT}.
|
||||
@c
|
||||
@c If the issuer's certificate has been found, the signature of the
|
||||
@c actual certificate is checked and in case this fails the error
|
||||
@c #code{GPG_ERR_BAD_CERT_CHAIN} is returned. If the signature checks out, the
|
||||
@c maximum cahin length of the issueing certificate is checked as well as
|
||||
@c the capiblity of the certificate (i.e. whether he may be used for
|
||||
@c certificate signing). Then the certificate is prepended to our list
|
||||
@c representing the certificate chain. Finally the loop is continued now
|
||||
@c with the issuer's certificate as the current certificate.
|
||||
@c
|
||||
@c After the end of the loop and if no error as been encountered
|
||||
@c (i.e. the certificate chain has been assempled correctly), a check is
|
||||
@c done whether any certificate expired or a critical policy has not been
|
||||
@c met. In any of these cases the validation terminates with an
|
||||
@c appropriate error.
|
||||
@c
|
||||
@c Finally the function @code{check_revocations} is called to verify no
|
||||
@c certificate in the assempled chain has been revoked: This is an
|
||||
@c recursive process because a CRL has to be checked for each certificate
|
||||
@c in the chain except for the root certificate, of which we already know
|
||||
@c that it is trusted and we avoid checking a CRL here due to common
|
||||
@c setup problems and the assumption that a revoked root certifcate has
|
||||
@c been removed from the list of trusted certificates.
|
||||
@c
|
||||
@c
|
||||
@c
|
||||
@c
|
||||
@c @section Looking up certificates through LDAP.
|
||||
@c
|
||||
@c This describes the LDAP layer to retrieve certificates.
|
||||
@c the functions @code{ca_cert_fetch} and @code{fetch_next_ksba_cert} are
|
||||
@c used for this. The first one starts a search and the second one is
|
||||
@c used to retrieve certificate after certificate.
|
||||
@c
|
||||
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
2010-06-17 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg.c (main): Use CAST5 as default s2k algo. The macro
|
||||
USE_CAST5 was only used with GnuPG 1.x.
|
||||
|
||||
2010-06-07 Werner Koch <wk@g10code.com>
|
||||
|
||||
* cpr.c: Use estream for status output.
|
||||
|
@ -1976,11 +1976,7 @@ main (int argc, char **argv)
|
||||
opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */
|
||||
opt.s2k_mode = 3; /* iterated+salted */
|
||||
opt.s2k_count = 0; /* Auto-calibrate when needed. */
|
||||
#ifdef USE_CAST5
|
||||
opt.s2k_cipher_algo = CIPHER_ALGO_CAST5;
|
||||
#else
|
||||
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
|
||||
#endif
|
||||
opt.completes_needed = 1;
|
||||
opt.marginals_needed = 3;
|
||||
opt.max_cert_depth = 5;
|
||||
|
16
sm/ChangeLog
16
sm/ChangeLog
@ -1,3 +1,19 @@
|
||||
2010-06-17 Werner Koch <wk@g10code.com>
|
||||
|
||||
* import.c (parse_p12): Remove arg retfp. Use the agent's new
|
||||
import command.
|
||||
(import_one): Adjust call to pkcs12.
|
||||
(store_cert_cb, rsa_key_check): New.
|
||||
(popen_protect_tool): Remove.
|
||||
* minip12.c (parse_bag_encrypted_data, p12_parse): Add arg
|
||||
R_BADPASS.
|
||||
* call-agent.c (gpgsm_agent_ask_passphrase): New.
|
||||
(gpgsm_agent_keywrap_key): New.
|
||||
(struct import_key_parm_s): New.
|
||||
(gpgsm_agent_import_key): New.
|
||||
* minip12.c, minip12.h: Move from ../agent/.
|
||||
* Makefile.am (gpgsm_SOURCES): Add them.
|
||||
|
||||
2010-06-11 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* server.c (cmd_message) [HAVE_W32CE_SYSTEM]: Finish pipe.
|
||||
|
@ -49,6 +49,7 @@ gpgsm_SOURCES = \
|
||||
delete.c \
|
||||
certreqgen.c \
|
||||
certreqgen-ui.c \
|
||||
minip12.c minip12.h \
|
||||
qualified.c
|
||||
|
||||
|
||||
|
137
sm/call-agent.c
137
sm/call-agent.c
@ -1,6 +1,6 @@
|
||||
/* call-agent.c - Divert GPGSM operations to the agent
|
||||
* Copyright (C) 2001, 2002, 2003, 2005, 2007,
|
||||
* 2008, 2009 Free Software Foundation, Inc.
|
||||
* 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -66,6 +66,14 @@ struct learn_parm_s
|
||||
membuf_t *data;
|
||||
};
|
||||
|
||||
struct import_key_parm_s
|
||||
{
|
||||
ctrl_t ctrl;
|
||||
assuan_context_t ctx;
|
||||
const void *key;
|
||||
size_t keylen;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Try to connect to the agent via socket or fork it off and work by
|
||||
@ -1066,3 +1074,130 @@ gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Ask for the passphrase (this is used for pkcs#12 import/export. 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
|
||||
error code returned. */
|
||||
gpg_error_t
|
||||
gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
|
||||
char **r_passphrase)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
char *arg4 = NULL;
|
||||
membuf_t data;
|
||||
|
||||
*r_passphrase = NULL;
|
||||
|
||||
err = start_agent (ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (desc_msg && *desc_msg && !(arg4 = percent_plus_escape (desc_msg)))
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data -- X X X %s", arg4);
|
||||
xfree (arg4);
|
||||
|
||||
init_membuf_secure (&data, 64);
|
||||
err = assuan_transact (agent_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
default_inq_cb, NULL, NULL, NULL);
|
||||
|
||||
if (err)
|
||||
xfree (get_membuf (&data, NULL));
|
||||
else
|
||||
{
|
||||
put_membuf (&data, "", 1);
|
||||
*r_passphrase = get_membuf (&data, NULL);
|
||||
if (!*r_passphrase)
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Retrieve a key encryption key from the agent. With FOREXPORT true
|
||||
the key shall be use for export, with false for import. On success
|
||||
the new key is stored at R_KEY and its length at R_KEKLEN. */
|
||||
gpg_error_t
|
||||
gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
|
||||
void **r_kek, size_t *r_keklen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
membuf_t data;
|
||||
size_t len;
|
||||
unsigned char *buf;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
|
||||
*r_kek = NULL;
|
||||
err = start_agent (ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
|
||||
forexport? "--export":"--import");
|
||||
|
||||
init_membuf_secure (&data, 64);
|
||||
err = assuan_transact (agent_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
default_inq_cb, ctrl, NULL, NULL);
|
||||
if (err)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
return err;
|
||||
}
|
||||
buf = get_membuf (&data, &len);
|
||||
if (!buf)
|
||||
return gpg_error_from_syserror ();
|
||||
*r_kek = buf;
|
||||
*r_keklen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Handle the inquiry for an IMPORT_KEY command. */
|
||||
static gpg_error_t
|
||||
inq_import_key_parms (void *opaque, const char *line)
|
||||
{
|
||||
struct import_key_parm_s *parm = opaque;
|
||||
gpg_error_t err;
|
||||
|
||||
if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
|
||||
{
|
||||
assuan_begin_confidential (parm->ctx);
|
||||
err = assuan_send_data (parm->ctx, parm->key, parm->keylen);
|
||||
assuan_end_confidential (parm->ctx);
|
||||
}
|
||||
else
|
||||
err = default_inq_cb (parm->ctrl, line);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Call the agent to import a key into the agent. */
|
||||
gpg_error_t
|
||||
gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
struct import_key_parm_s parm;
|
||||
|
||||
err = start_agent (ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
parm.ctrl = ctrl;
|
||||
parm.ctx = agent_ctx;
|
||||
parm.key = key;
|
||||
parm.keylen = keylen;
|
||||
|
||||
err = assuan_transact (agent_ctx, "IMPORT_KEY",
|
||||
NULL, NULL, inq_import_key_parms, &parm, NULL, NULL);
|
||||
return err;
|
||||
}
|
||||
|
@ -405,6 +405,12 @@ gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
|
||||
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
|
||||
gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
|
||||
char **r_serialno);
|
||||
gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
|
||||
char **r_passphrase);
|
||||
gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
|
||||
void **r_kek, size_t *r_keklen);
|
||||
gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl,
|
||||
const void *key, size_t keylen);
|
||||
|
||||
/*-- call-dirmngr.c --*/
|
||||
int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
|
||||
|
568
sm/import.c
568
sm/import.c
@ -35,6 +35,11 @@
|
||||
#include "i18n.h"
|
||||
#include "sysutils.h"
|
||||
#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
|
||||
#include "../common/membuf.h"
|
||||
#include "minip12.h"
|
||||
|
||||
/* The arbitrary limit of one PKCS#12 object. */
|
||||
#define MAX_P12OBJ_SIZE 128 /*kb*/
|
||||
|
||||
|
||||
struct stats_s {
|
||||
@ -48,8 +53,19 @@ struct stats_s {
|
||||
};
|
||||
|
||||
|
||||
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. */
|
||||
};
|
||||
|
||||
|
||||
static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
|
||||
estream_t *retfp, struct stats_s *stats);
|
||||
struct stats_s *stats);
|
||||
|
||||
|
||||
|
||||
@ -325,51 +341,11 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
|
||||
any = 1;
|
||||
}
|
||||
else if (ct == KSBA_CT_PKCS12)
|
||||
{ /* This seems to be a pkcs12 message. We use an external
|
||||
tool to parse the message and to store the private keys.
|
||||
We need to use a another reader here to parse the
|
||||
certificate we included in the p12 file; then we continue
|
||||
to look for other pkcs12 files (works only if they are in
|
||||
PEM format. */
|
||||
estream_t certfp;
|
||||
Base64Context b64p12rdr;
|
||||
ksba_reader_t p12rdr;
|
||||
|
||||
rc = parse_p12 (ctrl, reader, &certfp, stats);
|
||||
{
|
||||
/* This seems to be a pkcs12 message. */
|
||||
rc = parse_p12 (ctrl, reader, stats);
|
||||
if (!rc)
|
||||
{
|
||||
any = 1;
|
||||
|
||||
es_rewind (certfp);
|
||||
rc = gpgsm_create_reader (&b64p12rdr, ctrl, certfp, 1, &p12rdr);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't create reader: %s\n", gpg_strerror (rc));
|
||||
es_fclose (certfp);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ksba_cert_release (cert); cert = NULL;
|
||||
rc = ksba_cert_new (&cert);
|
||||
if (!rc)
|
||||
{
|
||||
rc = ksba_cert_read_der (cert, p12rdr);
|
||||
if (!rc)
|
||||
check_and_store (ctrl, stats, cert, 0);
|
||||
}
|
||||
ksba_reader_clear (p12rdr, NULL, NULL);
|
||||
}
|
||||
while (!rc && !gpgsm_reader_eof_seen (b64p12rdr));
|
||||
|
||||
if (gpg_err_code (rc) == GPG_ERR_EOF)
|
||||
rc = 0;
|
||||
gpgsm_destroy_reader (b64p12rdr);
|
||||
es_fclose (certfp);
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
any = 1;
|
||||
}
|
||||
else if (ct == KSBA_CT_NONE)
|
||||
{ /* Failed to identify this message - assume a certificate */
|
||||
@ -578,213 +554,363 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
|
||||
}
|
||||
|
||||
|
||||
/* Fork and exec the protect tool, connect the file descriptor of
|
||||
INFILE to stdin, return a new estream in STATUSFILE, write the
|
||||
output to OUTFILE and the pid of the process in PID. Returns 0 on
|
||||
success or an error code. */
|
||||
/* Check that the RSA secret key SKEY is valid. Swap parameters to
|
||||
the libgcrypt standard. */
|
||||
static gpg_error_t
|
||||
popen_protect_tool (ctrl_t ctrl, const char *pgmname,
|
||||
estream_t infile, estream_t outfile,
|
||||
estream_t *statusfile, pid_t *pid)
|
||||
rsa_key_check (struct rsa_secret_key_s *skey)
|
||||
{
|
||||
const char *argv[22];
|
||||
int i=0;
|
||||
int err = 0;
|
||||
gcry_mpi_t t = gcry_mpi_snew (0);
|
||||
gcry_mpi_t t1 = gcry_mpi_snew (0);
|
||||
gcry_mpi_t t2 = gcry_mpi_snew (0);
|
||||
gcry_mpi_t phi = gcry_mpi_snew (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. 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";
|
||||
argv[i++] = opt.homedir;
|
||||
argv[i++] = "--p12-import";
|
||||
argv[i++] = "--store";
|
||||
argv[i++] = "--no-fail-on-exist";
|
||||
argv[i++] = "--enable-status-msg";
|
||||
if (opt.fixed_passphrase)
|
||||
/* Check that n == p * q. */
|
||||
gcry_mpi_mul (t, skey->p, skey->q);
|
||||
if (gcry_mpi_cmp( t, skey->n) )
|
||||
{
|
||||
argv[i++] = "--passphrase";
|
||||
argv[i++] = opt.fixed_passphrase;
|
||||
log_error ("RSA oops: n != p * q\n");
|
||||
err++;
|
||||
}
|
||||
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 | 64),
|
||||
statusfile, pid);
|
||||
/* Check that p is less than q. */
|
||||
if (gcry_mpi_cmp (skey->p, skey->q) > 0)
|
||||
{
|
||||
gcry_mpi_t tmp;
|
||||
|
||||
log_info ("swapping secret primes\n");
|
||||
tmp = gcry_mpi_copy (skey->p);
|
||||
gcry_mpi_set (skey->p, skey->q);
|
||||
gcry_mpi_set (skey->q, tmp);
|
||||
gcry_mpi_release (tmp);
|
||||
/* Recompute u. */
|
||||
gcry_mpi_invm (skey->u, skey->p, skey->q);
|
||||
}
|
||||
|
||||
/* Check that e divides neither p-1 nor q-1. */
|
||||
gcry_mpi_sub_ui (t, skey->p, 1 );
|
||||
gcry_mpi_div (NULL, t, t, skey->e, 0);
|
||||
if (!gcry_mpi_cmp_ui( t, 0) )
|
||||
{
|
||||
log_error ("RSA oops: e divides p-1\n");
|
||||
err++;
|
||||
}
|
||||
gcry_mpi_sub_ui (t, skey->q, 1);
|
||||
gcry_mpi_div (NULL, t, t, skey->e, 0);
|
||||
if (!gcry_mpi_cmp_ui( t, 0))
|
||||
{
|
||||
log_info ("RSA oops: e divides q-1\n" );
|
||||
err++;
|
||||
}
|
||||
|
||||
/* Check that d is correct. */
|
||||
gcry_mpi_sub_ui (t1, skey->p, 1);
|
||||
gcry_mpi_sub_ui (t2, skey->q, 1);
|
||||
gcry_mpi_mul (phi, t1, t2);
|
||||
gcry_mpi_invm (t, skey->e, phi);
|
||||
if (gcry_mpi_cmp (t, skey->d))
|
||||
{
|
||||
/* No: try universal exponent. */
|
||||
gcry_mpi_gcd (t, t1, t2);
|
||||
gcry_mpi_div (t, NULL, phi, t, 0);
|
||||
gcry_mpi_invm (t, skey->e, t);
|
||||
if (gcry_mpi_cmp (t, skey->d))
|
||||
{
|
||||
log_error ("RSA oops: bad secret exponent\n");
|
||||
err++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for correctness of u. */
|
||||
gcry_mpi_invm (t, skey->p, skey->q);
|
||||
if (gcry_mpi_cmp (t, skey->u))
|
||||
{
|
||||
log_info ("RSA oops: bad u parameter\n");
|
||||
err++;
|
||||
}
|
||||
|
||||
if (err)
|
||||
log_info ("RSA secret key check failed\n");
|
||||
|
||||
gcry_mpi_release (t);
|
||||
gcry_mpi_release (t1);
|
||||
gcry_mpi_release (t2);
|
||||
gcry_mpi_release (phi);
|
||||
|
||||
return err? gpg_error (GPG_ERR_BAD_SECKEY):0;
|
||||
}
|
||||
|
||||
|
||||
/* Object passed to store_cert_cb. */
|
||||
struct store_cert_parm_s
|
||||
{
|
||||
gpg_error_t err; /* First error seen. */
|
||||
struct stats_s *stats; /* The stats object. */
|
||||
ctrl_t ctrl; /* The control object. */
|
||||
};
|
||||
|
||||
/* Helper to store the DER encoded certificate CERTDATA of length
|
||||
CERTDATALEN. */
|
||||
static void
|
||||
store_cert_cb (void *opaque,
|
||||
const unsigned char *certdata, size_t certdatalen)
|
||||
{
|
||||
struct store_cert_parm_s *parm = opaque;
|
||||
gpg_error_t err;
|
||||
ksba_cert_t cert;
|
||||
|
||||
err = ksba_cert_new (&cert);
|
||||
if (err)
|
||||
{
|
||||
if (!parm->err)
|
||||
parm->err = err;
|
||||
return;
|
||||
}
|
||||
|
||||
err = ksba_cert_init_from_mem (cert, certdata, certdatalen);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to parse a certificate: %s\n", gpg_strerror (err));
|
||||
if (!parm->err)
|
||||
parm->err = err;
|
||||
}
|
||||
else
|
||||
check_and_store (parm->ctrl, parm->stats, cert, 0);
|
||||
ksba_cert_release (cert);
|
||||
}
|
||||
|
||||
|
||||
/* Assume that the reader is at a pkcs#12 message and try to import
|
||||
certificates from that stupid format. We will also store secret
|
||||
keys. All of the pkcs#12 parsing and key storing is handled by the
|
||||
gpg-protect-tool, we merely have to take care of receiving the
|
||||
certificates. On success RETFP returns a stream to a temporary
|
||||
file with certificates. */
|
||||
certificates from that stupid format. We will transfer secret
|
||||
keys to the agent. */
|
||||
static gpg_error_t
|
||||
parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
|
||||
estream_t *retfp, struct stats_s *stats)
|
||||
parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats)
|
||||
{
|
||||
const char *pgmname;
|
||||
gpg_error_t err = 0, child_err = 0;
|
||||
int c, cont_line;
|
||||
unsigned int pos;
|
||||
estream_t tmpfp;
|
||||
estream_t fp = NULL;
|
||||
estream_t certfp = NULL;
|
||||
gpg_error_t err = 0;
|
||||
char buffer[1024];
|
||||
size_t nread;
|
||||
pid_t pid = -1;
|
||||
size_t ntotal, nread;
|
||||
membuf_t p12mbuf;
|
||||
char *p12buffer = NULL;
|
||||
size_t p12buflen;
|
||||
size_t p12bufoff;
|
||||
gcry_mpi_t *kparms = NULL;
|
||||
struct rsa_secret_key_s sk;
|
||||
char *passphrase = NULL;
|
||||
unsigned char *key = NULL;
|
||||
size_t keylen;
|
||||
void *kek = NULL;
|
||||
size_t keklen;
|
||||
unsigned char *wrappedkey = NULL;
|
||||
size_t wrappedkeylen;
|
||||
gcry_cipher_hd_t cipherhd = NULL;
|
||||
gcry_sexp_t s_key = NULL;
|
||||
unsigned char grip[20];
|
||||
int bad_pass = 0;
|
||||
int i;
|
||||
struct store_cert_parm_s store_cert_parm;
|
||||
|
||||
if (!opt.protect_tool_program || !*opt.protect_tool_program)
|
||||
pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
|
||||
else
|
||||
pgmname = opt.protect_tool_program;
|
||||
memset (&store_cert_parm, 0, sizeof store_cert_parm);
|
||||
store_cert_parm.ctrl = ctrl;
|
||||
store_cert_parm.stats = stats;
|
||||
|
||||
*retfp = NULL;
|
||||
|
||||
/* To avoid an extra feeder process or doing selects and because
|
||||
gpg-protect-tool will anyway parse the entire pkcs#12 message in
|
||||
memory, we simply use tempfiles here and pass them to
|
||||
the gpg-protect-tool. */
|
||||
tmpfp = es_tmpfile ();
|
||||
if (!tmpfp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error (_("error creating temporary file: %s\n"), strerror (errno));
|
||||
goto cleanup;
|
||||
}
|
||||
init_membuf (&p12mbuf, 4096);
|
||||
ntotal = 0;
|
||||
while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
|
||||
{
|
||||
if (nread && es_fwrite (buffer, nread, 1, tmpfp) != 1)
|
||||
if (ntotal >= MAX_P12OBJ_SIZE*1024)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error (_("error writing to temporary file: %s\n"),
|
||||
strerror (errno));
|
||||
goto cleanup;
|
||||
/* Arbitrary limit to avoid DoS attacks. */
|
||||
err = gpg_error (GPG_ERR_TOO_LARGE);
|
||||
log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE);
|
||||
break;
|
||||
}
|
||||
put_membuf (&p12mbuf, buffer, nread);
|
||||
ntotal += nread;
|
||||
}
|
||||
if (gpg_err_code (err) == GPG_ERR_EOF)
|
||||
err = 0;
|
||||
if (!err)
|
||||
{
|
||||
p12buffer = get_membuf (&p12mbuf, &p12buflen);
|
||||
if (!p12buffer)
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
log_error (_("error reading input: %s\n"), gpg_strerror (err));
|
||||
goto cleanup;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
certfp = es_tmpfile ();
|
||||
if (!certfp)
|
||||
/* GnuPG 2.0.4 accidently created binary P12 files with the string
|
||||
"The passphrase is %s encoded.\n\n" prepended to the ASN.1 data.
|
||||
We fix that here. */
|
||||
if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error (_("error creating temporary file: %s\n"), strerror (errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = popen_protect_tool (ctrl, pgmname, tmpfp, certfp, &fp, &pid);
|
||||
if (err)
|
||||
{
|
||||
pid = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
es_fclose (tmpfp);
|
||||
tmpfp = NULL;
|
||||
|
||||
/* Read stderr of the protect tool. */
|
||||
pos = 0;
|
||||
cont_line = 0;
|
||||
while ((c=es_getc (fp)) != EOF)
|
||||
{
|
||||
/* fixme: We could here grep for status information of the
|
||||
protect tool to figure out better error codes for
|
||||
CHILD_ERR. */
|
||||
buffer[pos++] = c;
|
||||
if (pos >= sizeof buffer - 5 || c == '\n')
|
||||
{
|
||||
buffer[pos - (c == '\n')] = 0;
|
||||
if (cont_line)
|
||||
log_printf ("%s", buffer);
|
||||
else
|
||||
{
|
||||
if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
|
||||
{
|
||||
char *p, *pend;
|
||||
|
||||
p = buffer + 34;
|
||||
pend = strchr (p, ' ');
|
||||
if (pend)
|
||||
*pend = 0;
|
||||
if ( !strcmp (p, "secretkey-stored"))
|
||||
{
|
||||
stats->count++;
|
||||
stats->secret_read++;
|
||||
stats->secret_imported++;
|
||||
}
|
||||
else if ( !strcmp (p, "secretkey-exists"))
|
||||
{
|
||||
stats->count++;
|
||||
stats->secret_read++;
|
||||
stats->secret_dups++;
|
||||
}
|
||||
else if ( !strcmp (p, "bad-passphrase"))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_info ("%s", buffer);
|
||||
if (!strncmp (buffer, "gpg-protect-tool: "
|
||||
"possibly bad passphrase given",46))
|
||||
bad_pass++;
|
||||
}
|
||||
}
|
||||
pos = 0;
|
||||
cont_line = (c != '\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
buffer[pos] = 0;
|
||||
if (cont_line)
|
||||
log_printf ("%s\n", buffer);
|
||||
else
|
||||
log_info ("%s\n", buffer);
|
||||
}
|
||||
|
||||
|
||||
/* If we found no error in the output of the child, setup a suitable
|
||||
error code, which will later be reset if the exit status of the
|
||||
child is 0. */
|
||||
if (!child_err)
|
||||
child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);
|
||||
|
||||
cleanup:
|
||||
es_fclose (tmpfp);
|
||||
es_fclose (fp);
|
||||
if (pid != -1)
|
||||
{
|
||||
if (!gnupg_wait_process (pgmname, pid, 0, NULL))
|
||||
child_err = 0;
|
||||
gnupg_release_process (pid);
|
||||
}
|
||||
if (!err)
|
||||
err = child_err;
|
||||
if (err)
|
||||
{
|
||||
es_fclose (certfp);
|
||||
for (p12bufoff=18;
|
||||
p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n';
|
||||
p12bufoff++)
|
||||
;
|
||||
p12bufoff++;
|
||||
if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n')
|
||||
p12bufoff++;
|
||||
}
|
||||
else
|
||||
*retfp = certfp;
|
||||
p12bufoff = 0;
|
||||
|
||||
|
||||
err = gpgsm_agent_ask_passphrase
|
||||
(ctrl, _("Please enter the passphrase to unprotect the PKCS#12 object."),
|
||||
&passphrase);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff,
|
||||
passphrase, store_cert_cb, &store_cert_parm, &bad_pass);
|
||||
|
||||
xfree (passphrase);
|
||||
passphrase = NULL;
|
||||
|
||||
if (!kparms)
|
||||
{
|
||||
log_error ("error parsing or decrypting the PKCS#12 file\n");
|
||||
err = gpg_error (GPG_ERR_INV_OBJ);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* print_mpi (" n", kparms[0]); */
|
||||
/* print_mpi (" e", kparms[1]); */
|
||||
/* print_mpi (" d", kparms[2]); */
|
||||
/* print_mpi (" p", kparms[3]); */
|
||||
/* print_mpi (" q", kparms[4]); */
|
||||
/* print_mpi ("dmp1", kparms[5]); */
|
||||
/* print_mpi ("dmq1", kparms[6]); */
|
||||
/* print_mpi (" u", kparms[7]); */
|
||||
|
||||
sk.n = kparms[0];
|
||||
sk.e = kparms[1];
|
||||
sk.d = kparms[2];
|
||||
sk.q = kparms[3];
|
||||
sk.p = kparms[4];
|
||||
sk.u = kparms[7];
|
||||
err = rsa_key_check (&sk);
|
||||
if (err)
|
||||
goto leave;
|
||||
/* print_mpi (" n", sk.n); */
|
||||
/* print_mpi (" e", sk.e); */
|
||||
/* print_mpi (" d", sk.d); */
|
||||
/* print_mpi (" p", sk.p); */
|
||||
/* print_mpi (" q", sk.q); */
|
||||
/* print_mpi (" u", sk.u); */
|
||||
|
||||
/* Create an S-expresion from the parameters. */
|
||||
err = gcry_sexp_build (&s_key, NULL,
|
||||
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
|
||||
sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL);
|
||||
for (i=0; i < 8; i++)
|
||||
gcry_mpi_release (kparms[i]);
|
||||
gcry_free (kparms);
|
||||
kparms = NULL;
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to created S-expression from key: %s\n",
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Compute the keygrip. */
|
||||
if (!gcry_pk_get_keygrip (s_key, grip))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
log_error ("can't calculate keygrip\n");
|
||||
goto leave;
|
||||
}
|
||||
log_printhex ("keygrip=", grip, 20);
|
||||
|
||||
/* Convert to canonical encoding using a function which pads it to a
|
||||
multiple of 64 bits. We need this padding for AESWRAP. */
|
||||
err = make_canon_sexp_pad (s_key, &key, &keylen);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error creating canonical S-expression\n");
|
||||
goto leave;
|
||||
}
|
||||
gcry_sexp_release (s_key);
|
||||
s_key = NULL;
|
||||
|
||||
/* Get the current KEK. */
|
||||
err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error getting the KEK: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Wrap the key. */
|
||||
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
|
||||
GCRY_CIPHER_MODE_AESWRAP, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = gcry_cipher_setkey (cipherhd, kek, keklen);
|
||||
if (err)
|
||||
goto leave;
|
||||
xfree (kek);
|
||||
kek = NULL;
|
||||
|
||||
wrappedkeylen = keylen + 8;
|
||||
wrappedkey = xtrymalloc (wrappedkeylen);
|
||||
if (!wrappedkey)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen);
|
||||
if (err)
|
||||
goto leave;
|
||||
xfree (key);
|
||||
key = NULL;
|
||||
gcry_cipher_close (cipherhd);
|
||||
cipherhd = NULL;
|
||||
|
||||
/* Send the wrapped key to the agent. */
|
||||
err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen);
|
||||
if (!err)
|
||||
{
|
||||
stats->count++;
|
||||
stats->secret_read++;
|
||||
stats->secret_imported++;
|
||||
}
|
||||
else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
|
||||
{
|
||||
err = 0;
|
||||
stats->count++;
|
||||
stats->secret_read++;
|
||||
stats->secret_dups++;
|
||||
}
|
||||
|
||||
/* If we did not get an error from storing the secret key we return
|
||||
a possible error from parsing the certificates. We do this after
|
||||
storing the secret keys so that a bad certificate does not
|
||||
inhibit our chance to store the secret key. */
|
||||
if (!err && store_cert_parm.err)
|
||||
err = store_cert_parm.err;
|
||||
|
||||
leave:
|
||||
if (kparms)
|
||||
{
|
||||
for (i=0; i < 8; i++)
|
||||
gcry_mpi_release (kparms[i]);
|
||||
gcry_free (kparms);
|
||||
kparms = NULL;
|
||||
}
|
||||
xfree (key);
|
||||
gcry_sexp_release (s_key);
|
||||
xfree (passphrase);
|
||||
gcry_cipher_close (cipherhd);
|
||||
xfree (wrappedkey);
|
||||
xfree (kek);
|
||||
xfree (get_membuf (&p12mbuf, NULL));
|
||||
xfree (p12buffer);
|
||||
|
||||
if (bad_pass)
|
||||
{
|
||||
|
@ -608,7 +608,8 @@ static int
|
||||
parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
|
||||
int startoffset, size_t *r_consumed, const char *pw,
|
||||
void (*certcb)(void*, const unsigned char*, size_t),
|
||||
void *certcbarg, gcry_mpi_t **r_result)
|
||||
void *certcbarg, gcry_mpi_t **r_result,
|
||||
int *r_badpass)
|
||||
{
|
||||
struct tag_info ti;
|
||||
const unsigned char *p = buffer;
|
||||
@ -1003,6 +1004,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
|
||||
to check for a bad passphrase; it should therefore not be
|
||||
translated or changed. */
|
||||
log_error ("possibly bad passphrase given\n");
|
||||
*r_badpass = 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1277,7 +1279,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
|
||||
gcry_mpi_t *
|
||||
p12_parse (const unsigned char *buffer, size_t length, const char *pw,
|
||||
void (*certcb)(void*, const unsigned char*, size_t),
|
||||
void *certcbarg)
|
||||
void *certcbarg, int *r_badpass)
|
||||
{
|
||||
struct tag_info ti;
|
||||
const unsigned char *p = buffer;
|
||||
@ -1289,6 +1291,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
|
||||
gcry_mpi_t *result = NULL;
|
||||
unsigned char *cram_buffer = NULL;
|
||||
|
||||
*r_badpass = 0;
|
||||
where = "pfx";
|
||||
if (parse_tag (&p, &n, &ti))
|
||||
goto bailout;
|
||||
@ -1384,7 +1387,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
|
||||
where = "bag.encryptedData";
|
||||
if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw,
|
||||
certcb, certcbarg,
|
||||
result? NULL : &result))
|
||||
result? NULL : &result, r_badpass))
|
||||
goto bailout;
|
||||
if (lenndef)
|
||||
len += consumed;
|
@ -25,7 +25,7 @@
|
||||
gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
|
||||
const char *pw,
|
||||
void (*certcb)(void*, const unsigned char*, size_t),
|
||||
void *certcbarg);
|
||||
void *certcbarg, int *r_badpass);
|
||||
|
||||
unsigned char *p12_build (gcry_mpi_t *kparms,
|
||||
unsigned char *cert, size_t certlen,
|
Loading…
x
Reference in New Issue
Block a user