1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

agent: Add "ephemeral" Assuan option.

* agent/agent.h (struct ephemeral_private_key_s): New.
(struct server_control_s): Add ephemeral_mode and ephemeral_keys.
(GENKEY_FLAG_NO_PROTECTION, GENKEY_FLAG_PRESET): New.
* agent/genkey.c (clear_ephemeral_keys): New.
(store_key): Add arg ctrl and implement ephemeral_mode.  Change all
callers.
(agent_genkey): Replace args no_protection and preset by a generic new
flags arg.
* agent/findkey.c (wipe_and_fclose): New.
(agent_write_private_key): Add arg ctrl and implement ephemeral_mode.
Change all callers.
(agent_update_private_key): Ditto
(read_key_file): Ditto.
(agent_key_available): Ditto.
* agent/command-ssh.c (card_key_available): Do not update display s/n
in ephemeral mode.  This is however enver triggred.
* agent/gpg-agent.c (agent_deinit_default_ctrl): Cleanup ephemeral
keys.
* agent/command.c (cmd_genkey): Use the new flags instead of separate
vars.
(cmd_readkey): Create a shadow key only in non-ephemeral_mode.
(cmd_getinfo): Add sub-command "ephemeral".
(option_handler): Add option "ephemeral".
--

The idea here that a session can be switched in an ephemeral mode
which does not store or read keys from disk but keeps them local to
the session.

GnuPG-bug-id: 6944
This commit is contained in:
Werner Koch 2024-01-22 13:22:44 +01:00
parent 18320d692c
commit 434a641d40
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
11 changed files with 496 additions and 206 deletions

View File

@ -225,6 +225,17 @@ typedef struct ssh_control_file_s *ssh_control_file_t;
/* Forward reference for local definitions in call-scd.c. */ /* Forward reference for local definitions in call-scd.c. */
struct daemon_local_s; struct daemon_local_s;
/* Object to hold ephemeral secret keys. */
struct ephemeral_private_key_s
{
struct ephemeral_private_key_s *next;
unsigned char grip[KEYGRIP_LEN];
unsigned char *keybuf; /* Canon-s-exp with the private key (malloced). */
size_t keybuflen;
};
typedef struct ephemeral_private_key_s *ephemeral_private_key_t;
/* Collection of data per session (aka connection). */ /* Collection of data per session (aka connection). */
struct server_control_s struct server_control_s
{ {
@ -246,6 +257,12 @@ struct server_control_s
/* Private data of the daemon (call-XXX.c). */ /* Private data of the daemon (call-XXX.c). */
struct daemon_local_s *d_local[DAEMON_MAX_TYPE]; struct daemon_local_s *d_local[DAEMON_MAX_TYPE];
/* Linked list with ephemeral stored private keys. */
ephemeral_private_key_t ephemeral_keys;
/* If set functions will lookup keys in the ephemeral_keys list. */
int ephemeral_mode;
/* Environment settings for the connection. */ /* Environment settings for the connection. */
session_env_t session_env; session_env_t session_env;
char *lc_ctype; char *lc_ctype;
@ -452,7 +469,8 @@ void start_command_handler_ssh (ctrl_t, gnupg_fd_t);
/*-- findkey.c --*/ /*-- findkey.c --*/
gpg_error_t agent_modify_description (const char *in, const char *comment, gpg_error_t agent_modify_description (const char *in, const char *comment,
const gcry_sexp_t key, char **result); const gcry_sexp_t key, char **result);
gpg_error_t agent_write_private_key (const unsigned char *grip, gpg_error_t agent_write_private_key (ctrl_t ctrl,
const unsigned char *grip,
const void *buffer, size_t length, const void *buffer, size_t length,
int force, int force,
const char *serialno, const char *keyref, const char *serialno, const char *keyref,
@ -477,7 +495,7 @@ gpg_error_t agent_ssh_key_from_file (ctrl_t ctrl,
gcry_sexp_t *result, int *r_order); gcry_sexp_t *result, int *r_order);
int agent_pk_get_algo (gcry_sexp_t s_key); int agent_pk_get_algo (gcry_sexp_t s_key);
int agent_is_tpm2_key(gcry_sexp_t s_key); int agent_is_tpm2_key(gcry_sexp_t s_key);
int agent_key_available (const unsigned char *grip); int agent_key_available (ctrl_t ctrl, const unsigned char *grip);
gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
int *r_keytype, int *r_keytype,
unsigned char **r_shadow_info, unsigned char **r_shadow_info,
@ -485,7 +503,8 @@ gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text, gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text,
const unsigned char *grip, const unsigned char *grip,
int force, int only_stubs); int force, int only_stubs);
gpg_error_t agent_update_private_key (const unsigned char *grip, nvc_t pk); gpg_error_t agent_update_private_key (ctrl_t ctrl,
const unsigned char *grip, nvc_t pk);
/*-- call-pinentry.c --*/ /*-- call-pinentry.c --*/
void initialize_module_call_pinentry (void); void initialize_module_call_pinentry (void);
@ -541,15 +560,21 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
#define CHECK_CONSTRAINTS_NOT_EMPTY 1 #define CHECK_CONSTRAINTS_NOT_EMPTY 1
#define CHECK_CONSTRAINTS_NEW_SYMKEY 2 #define CHECK_CONSTRAINTS_NEW_SYMKEY 2
#define GENKEY_FLAG_NO_PROTECTION 1
#define GENKEY_FLAG_PRESET 2
void clear_ephemeral_keys (ctrl_t ctrl);
int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int check_passphrase_constraints (ctrl_t ctrl, const char *pw,
unsigned int flags, unsigned int flags,
char **failed_constraint); char **failed_constraint);
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
char **r_passphrase); char **r_passphrase);
int agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp, int agent_genkey (ctrl_t ctrl, unsigned int flags,
const char *cache_nonce, time_t timestamp,
const char *keyparam, size_t keyparmlen, const char *keyparam, size_t keyparmlen,
int no_protection, const char *override_passphrase, const char *override_passphrase,
int preset, membuf_t *outbuf); membuf_t *outbuf);
gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
char **passphrase_addr); char **passphrase_addr);
@ -587,7 +612,7 @@ gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo,
const unsigned char *s2ksalt, const unsigned char *s2ksalt,
unsigned int s2kcount, unsigned int s2kcount,
unsigned char *key, size_t keylen); unsigned char *key, size_t keylen);
gpg_error_t agent_write_shadow_key (const unsigned char *grip, gpg_error_t agent_write_shadow_key (ctrl_t ctrl, const unsigned char *grip,
const char *serialno, const char *keyid, const char *serialno, const char *keyid,
const unsigned char *pkbuf, int force, const unsigned char *pkbuf, int force,
const char *dispserialno); const char *dispserialno);

View File

@ -2430,14 +2430,14 @@ card_key_available (ctrl_t ctrl, const struct card_key_info_s *keyinfo,
} }
hex2bin (keyinfo->keygrip, grip, sizeof (grip)); hex2bin (keyinfo->keygrip, grip, sizeof (grip));
if ( agent_key_available (grip) ) if (!ctrl->ephemeral_mode && agent_key_available (ctrl, grip) )
{ {
char *dispserialno; char *dispserialno;
/* (Shadow)-key is not available in our key storage. */ /* (Shadow)-key is not available in our key storage. */
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno, agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno,
keyinfo->keygrip); keyinfo->keygrip);
err = agent_write_shadow_key (grip, keyinfo->serialno, err = agent_write_shadow_key (ctrl, grip, keyinfo->serialno,
keyinfo->idstr, pkbuf, 0, dispserialno); keyinfo->idstr, pkbuf, 0, dispserialno);
xfree (dispserialno); xfree (dispserialno);
if (err) if (err)
@ -3222,7 +3222,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
/* Check whether the key is already in our key storage. Don't do /* Check whether the key is already in our key storage. Don't do
anything then besides (re-)adding it to sshcontrol. */ anything then besides (re-)adding it to sshcontrol. */
if ( !agent_key_available (key_grip_raw) ) if ( !agent_key_available (ctrl, key_grip_raw) )
goto key_exists; /* Yes, key is available. */ goto key_exists; /* Yes, key is available. */
err = ssh_key_extract_comment (key, &comment); err = ssh_key_extract_comment (key, &comment);
@ -3286,7 +3286,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
/* Store this key to our key storage. We do not store a creation /* Store this key to our key storage. We do not store a creation
* timestamp because we simply do not know. */ * timestamp because we simply do not know. */
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, err = agent_write_private_key (ctrl, key_grip_raw, buffer, buffer_n, 0,
NULL, NULL, NULL, 0); NULL, NULL, NULL, 0);
if (err) if (err)
goto out; goto out;

View File

@ -251,6 +251,9 @@ reset_notify (assuan_context_t ctx, char *line)
clear_nonce_cache (ctrl); clear_nonce_cache (ctrl);
/* Note that a RESET does not clear the ephemeral store becuase
* clients are used to issue a RESET on a connection. */
return 0; return 0;
} }
@ -643,15 +646,15 @@ static const char hlp_havekey[] =
static gpg_error_t static gpg_error_t
cmd_havekey (assuan_context_t ctx, char *line) cmd_havekey (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl; ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err; gpg_error_t err;
unsigned char grip[20]; unsigned char grip[20];
char *p; char *p;
int list_mode = 0; /* Less than 0 for no limit. */ int list_mode = 0; /* Less than 0 for no limit. */
int info_mode = 0; int info_mode = 0;
int counter; int counter;
char *dirname; char *dirname = NULL;
gnupg_dir_t dir; gnupg_dir_t dir = NULL;
gnupg_dirent_t dir_entry; gnupg_dirent_t dir_entry;
char hexgrip[41]; char hexgrip[41];
struct card_key_info_s *keyinfo_on_cards, *l; struct card_key_info_s *keyinfo_on_cards, *l;
@ -668,14 +671,11 @@ cmd_havekey (assuan_context_t ctx, char *line)
line = skip_options (line); line = skip_options (line);
if (info_mode) if (info_mode)
{ {
int keytype; int keytype;
const char *infostring; const char *infostring;
ctrl = assuan_get_pointer (ctx);
err = parse_keygrip (ctx, line, grip); err = parse_keygrip (ctx, line, grip);
if (err) if (err)
goto leave; goto leave;
@ -706,7 +706,7 @@ cmd_havekey (assuan_context_t ctx, char *line)
if (err) if (err)
return err; return err;
if (!agent_key_available (grip)) if (!agent_key_available (ctrl, grip))
return 0; /* Found. */ return 0; /* Found. */
while (*line && *line != ' ' && *line != '\t') while (*line && *line != ' ' && *line != '\t')
@ -724,7 +724,6 @@ cmd_havekey (assuan_context_t ctx, char *line)
/* List mode. */ /* List mode. */
dir = NULL; dir = NULL;
dirname = NULL; dirname = NULL;
ctrl = assuan_get_pointer (ctx);
if (ctrl->restricted) if (ctrl->restricted)
{ {
@ -1117,26 +1116,27 @@ cmd_genkey (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; int rc;
int no_protection;
unsigned char *value = NULL; unsigned char *value = NULL;
size_t valuelen; size_t valuelen;
unsigned char *newpasswd = NULL; unsigned char *newpasswd = NULL;
membuf_t outbuf; membuf_t outbuf;
char *cache_nonce = NULL; char *cache_nonce = NULL;
char *passwd_nonce = NULL; char *passwd_nonce = NULL;
int opt_preset;
int opt_inq_passwd; int opt_inq_passwd;
size_t n; size_t n;
char *p, *pend; char *p, *pend;
const char *s; const char *s;
time_t opt_timestamp; time_t opt_timestamp;
int c; int c;
unsigned int flags = 0;
if (ctrl->restricted) if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
no_protection = has_option (line, "--no-protection"); if (has_option (line, "--no-protection"))
opt_preset = has_option (line, "--preset"); flags |= GENKEY_FLAG_NO_PROTECTION;
if (has_option (line, "--preset"))
flags |= GENKEY_FLAG_PRESET;
opt_inq_passwd = has_option (line, "--inq-passwd"); opt_inq_passwd = has_option (line, "--inq-passwd");
passwd_nonce = option_value (line, "--passwd-nonce"); passwd_nonce = option_value (line, "--passwd-nonce");
if (passwd_nonce) if (passwd_nonce)
@ -1191,7 +1191,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
/* If requested, ask for the password to be used for the key. If /* If requested, ask for the password to be used for the key. If
this is not used the regular Pinentry mechanism is used. */ this is not used the regular Pinentry mechanism is used. */
if (opt_inq_passwd && !no_protection) if (opt_inq_passwd && !(flags & GENKEY_FLAG_NO_PROTECTION))
{ {
/* (N is used as a dummy) */ /* (N is used as a dummy) */
assuan_begin_confidential (ctx); assuan_begin_confidential (ctx);
@ -1204,16 +1204,17 @@ cmd_genkey (assuan_context_t ctx, char *line)
/* Empty password given - switch to no-protection mode. */ /* Empty password given - switch to no-protection mode. */
xfree (newpasswd); xfree (newpasswd);
newpasswd = NULL; newpasswd = NULL;
no_protection = 1; flags |= GENKEY_FLAG_NO_PROTECTION;
} }
} }
else if (passwd_nonce) else if (passwd_nonce)
newpasswd = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE); newpasswd = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE);
rc = agent_genkey (ctrl, cache_nonce, opt_timestamp,
(char*)value, valuelen, no_protection, rc = agent_genkey (ctrl, flags, cache_nonce, opt_timestamp,
newpasswd, opt_preset, &outbuf); (char*)value, valuelen,
newpasswd, &outbuf);
leave: leave:
if (newpasswd) if (newpasswd)
@ -1317,7 +1318,7 @@ cmd_keyattr (assuan_context_t ctx, char *line)
if (!err) if (!err)
err = nvc_set_private_key (keymeta, s_key); err = nvc_set_private_key (keymeta, s_key);
if (!err) if (!err)
err = agent_update_private_key (grip, keymeta); err = agent_update_private_key (ctrl, grip, keymeta);
} }
nvc_release (keymeta); nvc_release (keymeta);
@ -1327,6 +1328,8 @@ cmd_keyattr (assuan_context_t ctx, char *line)
leave: leave:
return leave_cmd (ctx, err); return leave_cmd (ctx, err);
} }
static const char hlp_readkey[] = static const char hlp_readkey[] =
"READKEY [--no-data] [--format=ssh] <hexstring_with_keygrip>\n" "READKEY [--no-data] [--format=ssh] <hexstring_with_keygrip>\n"
@ -1390,7 +1393,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
goto leave; goto leave;
} }
if (agent_key_available (grip)) if (!ctrl->ephemeral_mode && agent_key_available (ctrl, grip))
{ {
/* (Shadow)-key is not available in our key storage. */ /* (Shadow)-key is not available in our key storage. */
char *dispserialno; char *dispserialno;
@ -1398,7 +1401,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
bin2hex (grip, 20, hexgrip); bin2hex (grip, 20, hexgrip);
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno, hexgrip); agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno, hexgrip);
rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0, rc = agent_write_shadow_key (ctrl, grip, serialno, keyid, pkbuf, 0,
dispserialno); dispserialno);
xfree (dispserialno); xfree (dispserialno);
if (rc) if (rc)
@ -2934,7 +2937,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
} }
else else
{ {
if (!force && !agent_key_available (grip)) if (!force && !agent_key_available (ctrl, grip))
err = gpg_error (GPG_ERR_EEXIST); err = gpg_error (GPG_ERR_EEXIST);
else else
{ {
@ -2956,11 +2959,11 @@ cmd_import_key (assuan_context_t ctx, char *line)
err = agent_protect (key, passphrase, &finalkey, &finalkeylen, err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
ctrl->s2k_count); ctrl->s2k_count);
if (!err) if (!err)
err = agent_write_private_key (grip, finalkey, finalkeylen, force, err = agent_write_private_key (ctrl, grip, finalkey, finalkeylen, force,
NULL, NULL, NULL, opt_timestamp); NULL, NULL, NULL, opt_timestamp);
} }
else else
err = agent_write_private_key (grip, key, realkeylen, force, err = agent_write_private_key (ctrl, grip, key, realkeylen, force,
NULL, NULL, NULL, opt_timestamp); NULL, NULL, NULL, opt_timestamp);
leave: leave:
@ -2979,7 +2982,8 @@ cmd_import_key (assuan_context_t ctx, char *line)
static const char hlp_export_key[] = static const char hlp_export_key[] =
"EXPORT_KEY [--cache-nonce=<nonce>] [--openpgp|--mode1003] <hexkeygrip>\n" "EXPORT_KEY [--cache-nonce=<nonce>] \\\n"
" [--openpgp|--mode1003] <hexkeygrip>\n"
"\n" "\n"
"Export a secret key from the key store. The key will be encrypted\n" "Export a secret key from the key store. The key will be encrypted\n"
"using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n" "using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n"
@ -2987,8 +2991,8 @@ static const char hlp_export_key[] =
"prior to using this command. The function takes the keygrip as argument.\n" "prior to using this command. The function takes the keygrip as argument.\n"
"\n" "\n"
"If --openpgp is used, the secret key material will be exported in RFC 4880\n" "If --openpgp is used, the secret key material will be exported in RFC 4880\n"
"compatible passphrase-protected form. If --mode1003 is use the secret key\n" "compatible passphrase-protected form. In --mode1003 the secret key\n"
"is exported as s-expression as storred locally. Without those options,\n" "is exported as s-expression as stored locally. Without those options,\n"
"the secret key material will be exported in the clear (after prompting\n" "the secret key material will be exported in the clear (after prompting\n"
"the user to unlock it, if needed).\n"; "the user to unlock it, if needed).\n";
static gpg_error_t static gpg_error_t
@ -3045,7 +3049,7 @@ cmd_export_key (assuan_context_t ctx, char *line)
if (err) if (err)
goto leave; goto leave;
if (agent_key_available (grip)) if (agent_key_available (ctrl, grip))
{ {
err = gpg_error (GPG_ERR_NO_SECKEY); err = gpg_error (GPG_ERR_NO_SECKEY);
goto leave; goto leave;
@ -3257,7 +3261,7 @@ cmd_keytocard (assuan_context_t ctx, char *line)
if (err) if (err)
goto leave; goto leave;
if (agent_key_available (grip)) if (agent_key_available (ctrl, grip))
{ {
err = gpg_error (GPG_ERR_NO_SECKEY); err = gpg_error (GPG_ERR_NO_SECKEY);
goto leave; goto leave;
@ -3577,7 +3581,7 @@ cmd_keytotpm (assuan_context_t ctx, char *line)
if (err) if (err)
goto leave; goto leave;
if (agent_key_available (grip)) if (agent_key_available (ctrl, grip))
{ {
err =gpg_error (GPG_ERR_NO_SECKEY); err =gpg_error (GPG_ERR_NO_SECKEY);
goto leave; goto leave;
@ -3869,6 +3873,7 @@ static const char hlp_getinfo[] =
" getenv NAME - Return value of envvar NAME.\n" " getenv NAME - Return value of envvar NAME.\n"
" connections - Return number of active connections.\n" " connections - Return number of active connections.\n"
" jent_active - Returns OK if Libgcrypt's JENT is active.\n" " jent_active - Returns OK if Libgcrypt's JENT is active.\n"
" ephemeral - Returns OK if the connection is in ephemeral mode.\n"
" restricted - Returns OK if the connection is in restricted mode.\n" " restricted - Returns OK if the connection is in restricted mode.\n"
" cmd_has_option CMD OPT\n" " cmd_has_option CMD OPT\n"
" - Returns OK if command CMD has option OPT.\n"; " - Returns OK if command CMD has option OPT.\n";
@ -3922,6 +3927,10 @@ cmd_getinfo (assuan_context_t ctx, char *line)
snprintf (numbuf, sizeof numbuf, "%lu", get_standard_s2k_count ()); snprintf (numbuf, sizeof numbuf, "%lu", get_standard_s2k_count ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
} }
else if (!strcmp (line, "ephemeral"))
{
rc = ctrl->ephemeral_mode? 0 : gpg_error (GPG_ERR_FALSE);
}
else if (!strcmp (line, "restricted")) else if (!strcmp (line, "restricted"))
{ {
rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_FALSE); rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_FALSE);
@ -4078,6 +4087,10 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
ctrl->server_local->allow_fully_canceled = ctrl->server_local->allow_fully_canceled =
gnupg_compare_version (value, "2.1.0"); gnupg_compare_version (value, "2.1.0");
} }
else if (!strcmp (key, "ephemeral"))
{
ctrl->ephemeral_mode = *value? atoi (value) : 0;
}
else if (ctrl->restricted) else if (ctrl->restricted)
{ {
err = gpg_error (GPG_ERR_FORBIDDEN); err = gpg_error (GPG_ERR_FORBIDDEN);

View File

@ -969,7 +969,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
if (err) if (err)
goto leave; goto leave;
if (!dontcare_exist && !from_native && !agent_key_available (grip)) if (!dontcare_exist && !from_native && !agent_key_available (ctrl, grip))
{ {
err = gpg_error (GPG_ERR_EEXIST); err = gpg_error (GPG_ERR_EEXIST);
goto leave; goto leave;
@ -1147,14 +1147,16 @@ convert_from_openpgp_native (ctrl_t ctrl,
if (!agent_protect (*r_key, passphrase, if (!agent_protect (*r_key, passphrase,
&protectedkey, &protectedkeylen, &protectedkey, &protectedkeylen,
ctrl->s2k_count)) ctrl->s2k_count))
agent_write_private_key (grip, protectedkey, protectedkeylen, 1, agent_write_private_key (ctrl, grip,
NULL, NULL, NULL, 0); protectedkey,
protectedkeylen,
1, NULL, NULL, NULL, 0);
xfree (protectedkey); xfree (protectedkey);
} }
else else
{ {
/* Empty passphrase: write key without protection. */ /* Empty passphrase: write key without protection. */
agent_write_private_key (grip, agent_write_private_key (ctrl, grip,
*r_key, *r_key,
gcry_sexp_canon_len (*r_key, 0, NULL,NULL), gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
1, NULL, NULL, NULL, 0); 1, NULL, NULL, NULL, 0);

View File

@ -57,7 +57,7 @@ agent_write_tpm2_shadow_key (ctrl_t ctrl, const unsigned char *grip,
} }
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
err = agent_write_private_key (grip, shdkey, len, 1 /*force*/, err = agent_write_private_key (ctrl, grip, shdkey, len, 1 /*force*/,
NULL, NULL, NULL, 0); NULL, NULL, NULL, 0);
xfree (shdkey); xfree (shdkey);
if (err) if (err)
@ -70,7 +70,7 @@ agent_write_tpm2_shadow_key (ctrl_t ctrl, const unsigned char *grip,
return GPG_ERR_ENOMEM; return GPG_ERR_ENOMEM;
gcry_sexp_sprint(s_key, GCRYSEXP_FMT_CANON, pkbuf, len); gcry_sexp_sprint(s_key, GCRYSEXP_FMT_CANON, pkbuf, len);
err1 = agent_write_private_key (grip, pkbuf, len, 1 /*force*/, err1 = agent_write_private_key (ctrl, grip, pkbuf, len, 1 /*force*/,
NULL, NULL, NULL, 0); NULL, NULL, NULL, 0);
xfree(pkbuf); xfree(pkbuf);
if (err1) if (err1)

View File

@ -40,7 +40,7 @@
#endif #endif
static gpg_error_t read_key_file (const unsigned char *grip, static gpg_error_t read_key_file (ctrl_t ctrl, const unsigned char *grip,
gcry_sexp_t *result, nvc_t *r_keymeta, gcry_sexp_t *result, nvc_t *r_keymeta,
char **r_orig_key_value); char **r_orig_key_value);
static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey); static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey);
@ -73,6 +73,30 @@ fname_from_keygrip (const unsigned char *grip, int for_new)
} }
/* Helper until we have a "wipe" mode flag in es_fopen. */
static void
wipe_and_fclose (estream_t fp)
{
void *blob;
size_t blob_size;
if (!fp)
;
else if (es_fclose_snatch (fp, &blob, &blob_size))
{
log_error ("error wiping buffer during fclose\n");
es_fclose (fp);
}
else if (blob)
{
wipememory (blob, blob_size);
gpgrt_free (blob);
}
}
/* Replace all linefeeds in STRING by "%0A" and return a new malloced /* Replace all linefeeds in STRING by "%0A" and return a new malloced
* string. May return NULL on memory error. */ * string. May return NULL on memory error. */
static char * static char *
@ -110,7 +134,8 @@ linefeed_to_percent0A (const char *string)
* TIMESTAMP is not zero and the key does not yet exists it will be * TIMESTAMP is not zero and the key does not yet exists it will be
* recorded as creation date. */ * recorded as creation date. */
gpg_error_t gpg_error_t
agent_write_private_key (const unsigned char *grip, agent_write_private_key (ctrl_t ctrl,
const unsigned char *grip,
const void *buffer, size_t length, int force, const void *buffer, size_t length, int force,
const char *serialno, const char *keyref, const char *serialno, const char *keyref,
const char *dispserialno, const char *dispserialno,
@ -120,7 +145,7 @@ agent_write_private_key (const unsigned char *grip,
char *fname = NULL; char *fname = NULL;
char *tmpfname = NULL; char *tmpfname = NULL;
estream_t fp = NULL; estream_t fp = NULL;
int newkey; int newkey = 0;
nvc_t pk = NULL; nvc_t pk = NULL;
gcry_sexp_t key = NULL; gcry_sexp_t key = NULL;
int removetmp = 0; int removetmp = 0;
@ -134,11 +159,13 @@ agent_write_private_key (const unsigned char *grip,
const char *s; const char *s;
int force_modify = 0; int force_modify = 0;
fname = fname_from_keygrip (grip, 0); fname = (ctrl->ephemeral_mode
? xtrystrdup ("[ephemeral key store]")
: fname_from_keygrip (grip, 0));
if (!fname) if (!fname)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
err = read_key_file (grip, &key, &pk, &orig_key_value); err = read_key_file (ctrl, grip, &key, &pk, &orig_key_value);
if (err) if (err)
{ {
if (gpg_err_code (err) == GPG_ERR_ENOENT) if (gpg_err_code (err) == GPG_ERR_ENOENT)
@ -289,6 +316,61 @@ agent_write_private_key (const unsigned char *grip,
goto leave; goto leave;
} }
if (ctrl->ephemeral_mode)
{
ephemeral_private_key_t ek;
void *blob;
size_t blobsize;
for (ek = ctrl->ephemeral_keys; ek; ek = ek->next)
if (!memcmp (ek->grip, grip, KEYGRIP_LEN))
break;
if (!ek)
{
ek = xtrycalloc (1, sizeof *ek);
if (!ek)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (ek->grip, grip, KEYGRIP_LEN);
ek->next = ctrl->ephemeral_keys;
ctrl->ephemeral_keys = ek;
}
fp = es_fopenmem (0, "wb,wipe");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("can't open memory stream: %s\n", gpg_strerror (err));
goto leave;
}
err = nvc_write (pk, fp);
if (err)
{
log_error ("error writing to memory stream: %s\n",gpg_strerror (err));
goto leave;
}
if (es_fclose_snatch (fp, &blob, &blobsize) || !blob)
{
err = gpg_error_from_syserror ();
log_error ("error getting memory stream buffer: %s\n",
gpg_strerror (err));
/* Closing right away so that we don't try another snatch in
* the cleanup. */
es_fclose (fp);
fp = NULL;
goto leave;
}
fp = NULL;
xfree (ek->keybuf);
ek->keybuf = blob;
ek->keybuflen = blobsize;
}
else
{
/* Create a temporary file for writing. */ /* Create a temporary file for writing. */
tmpfname = fname_from_keygrip (grip, 1); tmpfname = fname_from_keygrip (grip, 1);
fp = tmpfname ? es_fopen (tmpfname, "wbx,mode=-rw") : NULL; fp = tmpfname ? es_fopen (tmpfname, "wbx,mode=-rw") : NULL;
@ -327,12 +409,16 @@ agent_write_private_key (const unsigned char *grip,
removetmp = 1; removetmp = 1;
goto leave; goto leave;
} }
}
bump_key_eventcounter (); bump_key_eventcounter ();
leave: leave:
if (blocksigs) if (blocksigs)
gnupg_unblock_all_signals (); gnupg_unblock_all_signals ();
if (ctrl->ephemeral_mode)
wipe_and_fclose (fp);
else
es_fclose (fp); es_fclose (fp);
if (removetmp && tmpfname) if (removetmp && tmpfname)
gnupg_remove (tmpfname); gnupg_remove (tmpfname);
@ -350,7 +436,7 @@ agent_write_private_key (const unsigned char *grip,
gpg_error_t gpg_error_t
agent_update_private_key (const unsigned char *grip, nvc_t pk) agent_update_private_key (ctrl_t ctrl, const unsigned char *grip, nvc_t pk)
{ {
gpg_error_t err; gpg_error_t err;
char *fname0 = NULL; /* The existing file name. */ char *fname0 = NULL; /* The existing file name. */
@ -359,6 +445,57 @@ agent_update_private_key (const unsigned char *grip, nvc_t pk)
int removetmp = 0; int removetmp = 0;
int blocksigs = 0; int blocksigs = 0;
if (ctrl->ephemeral_mode)
{
ephemeral_private_key_t ek;
void *blob;
size_t blobsize;
for (ek = ctrl->ephemeral_keys; ek; ek = ek->next)
if (!memcmp (ek->grip, grip, KEYGRIP_LEN))
break;
if (!ek)
{
err = gpg_error (GPG_ERR_ENOENT);
goto leave;
}
fp = es_fopenmem (0, "wbx,wipe");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("can't open memory stream: %s\n", gpg_strerror (err));
goto leave;
}
err = nvc_write (pk, fp);
if (err)
{
log_error ("error writing to memory stream: %s\n",gpg_strerror (err));
goto leave;
}
if (es_fclose_snatch (fp, &blob, &blobsize) || !blob)
{
err = gpg_error_from_syserror ();
log_error ("error getting memory stream buffer: %s\n",
gpg_strerror (err));
/* Closing right away so that we don't try another snatch in
* the cleanup. */
es_fclose (fp);
fp = NULL;
goto leave;
}
fp = NULL;
/* No need to revisit the linked list because the found EK is
* not expected to change due to the other syscalls above. */
xfree (ek->keybuf);
ek->keybuf = blob;
ek->keybuflen = blobsize;
goto leave;
}
fname0 = fname_from_keygrip (grip, 0); fname0 = fname_from_keygrip (grip, 0);
if (!fname0) if (!fname0)
{ {
@ -404,6 +541,9 @@ agent_update_private_key (const unsigned char *grip, nvc_t pk)
leave: leave:
if (blocksigs) if (blocksigs)
gnupg_unblock_all_signals (); gnupg_unblock_all_signals ();
if (ctrl->ephemeral_mode)
wipe_and_fclose (fp);
else
es_fclose (fp); es_fclose (fp);
if (removetmp && fname) if (removetmp && fname)
gnupg_remove (fname); gnupg_remove (fname);
@ -888,17 +1028,17 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
* caller must free it. On failure returns an error code and stores * caller must free it. On failure returns an error code and stores
* NULL at RESULT and R_KEYMETA. */ * NULL at RESULT and R_KEYMETA. */
static gpg_error_t static gpg_error_t
read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta, read_key_file (ctrl_t ctrl, const unsigned char *grip,
char **r_orig_key_value) gcry_sexp_t *result, nvc_t *r_keymeta, char **r_orig_key_value)
{ {
gpg_error_t err; gpg_error_t err;
char *fname; char *fname;
estream_t fp; estream_t fp = NULL;
struct stat st; unsigned char *buf = NULL;
unsigned char *buf;
size_t buflen, erroff; size_t buflen, erroff;
gcry_sexp_t s_skey; nvc_t pk = NULL;
char first; char first;
size_t keybuflen;
*result = NULL; *result = NULL;
if (r_keymeta) if (r_keymeta)
@ -906,19 +1046,42 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
if (r_orig_key_value) if (r_orig_key_value)
*r_orig_key_value = NULL; *r_orig_key_value = NULL;
fname = fname_from_keygrip (grip, 0); fname = (ctrl->ephemeral_mode
? xtrystrdup ("[ephemeral key store]")
: fname_from_keygrip (grip, 0));
if (!fname) if (!fname)
{ {
return gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave;
} }
if (ctrl->ephemeral_mode)
{
ephemeral_private_key_t ek;
for (ek = ctrl->ephemeral_keys; ek; ek = ek->next)
if (!memcmp (ek->grip, grip, KEYGRIP_LEN)
&& ek->keybuf && ek->keybuflen)
break;
if (!ek || !ek->keybuf || !ek->keybuflen)
{
err = gpg_error (GPG_ERR_ENOENT);
goto leave;
}
keybuflen = ek->keybuflen;
fp = es_fopenmem_init (0, "rb", ek->keybuf, ek->keybuflen);
}
else
{
keybuflen = 0; /* Indicates that this is not ephemeral mode. */
fp = es_fopen (fname, "rb"); fp = es_fopen (fname, "rb");
}
if (!fp) if (!fp)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
if (gpg_err_code (err) != GPG_ERR_ENOENT) if (gpg_err_code (err) != GPG_ERR_ENOENT)
log_error ("can't open '%s': %s\n", fname, gpg_strerror (err)); log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
xfree (fname); goto leave;
return err;
} }
if (es_fread (&first, 1, 1, fp) != 1) if (es_fread (&first, 1, 1, fp) != 1)
@ -926,28 +1089,22 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error ("error reading first byte from '%s': %s\n", log_error ("error reading first byte from '%s': %s\n",
fname, gpg_strerror (err)); fname, gpg_strerror (err));
xfree (fname); goto leave;
es_fclose (fp);
return err;
} }
if (es_fseek (fp, 0, SEEK_SET)) if (es_fseek (fp, 0, SEEK_SET))
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err)); log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err));
xfree (fname); goto leave;
es_fclose (fp);
return err;
} }
if (first != '(') if (first != '(')
{ {
/* Key is in extended format. */ /* Key is in extended format. */
nvc_t pk = NULL;
int line; int line;
err = nvc_parse_private_key (&pk, &line, fp); err = nvc_parse_private_key (&pk, &line, fp);
es_fclose (fp);
if (err) if (err)
log_error ("error parsing '%s' line %d: %s\n", log_error ("error parsing '%s' line %d: %s\n",
@ -969,9 +1126,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
if (!*r_orig_key_value) if (!*r_orig_key_value)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
nvc_release (pk); goto leave;
xfree (fname);
return err;
} }
} }
} }
@ -979,35 +1134,31 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
} }
} }
if (!err && r_keymeta) goto leave; /* Ready. */
*r_keymeta = pk;
else
nvc_release (pk);
xfree (fname);
return err;
} }
if (keybuflen)
buflen = keybuflen;
else
{
struct stat st;
if (fstat (es_fileno (fp), &st)) if (fstat (es_fileno (fp), &st))
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err)); log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
xfree (fname); goto leave;
es_fclose (fp); }
return err; buflen = st.st_size;
} }
buflen = st.st_size;
buf = xtrymalloc (buflen+1); buf = xtrymalloc (buflen+1);
if (!buf) if (!buf)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error ("error allocating %zu bytes for '%s': %s\n", log_error ("error allocating %zu bytes for '%s': %s\n",
buflen, fname, gpg_strerror (err)); buflen, fname, gpg_strerror (err));
xfree (fname); goto leave;
es_fclose (fp);
xfree (buf);
return err;
} }
if (es_fread (buf, buflen, 1, fp) != 1) if (es_fread (buf, buflen, 1, fp) != 1)
@ -1015,25 +1166,32 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error ("error reading %zu bytes from '%s': %s\n", log_error ("error reading %zu bytes from '%s': %s\n",
buflen, fname, gpg_strerror (err)); buflen, fname, gpg_strerror (err));
xfree (fname); goto leave;
es_fclose (fp);
xfree (buf);
return err;
} }
/* Convert the file into a gcrypt S-expression object. */ /* Convert the file into a gcrypt S-expression object. */
err = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
xfree (fname);
es_fclose (fp);
xfree (buf);
if (err)
{ {
gcry_sexp_t s_skey;
err = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
if (err)
log_error ("failed to build S-Exp (off=%u): %s\n", log_error ("failed to build S-Exp (off=%u): %s\n",
(unsigned int)erroff, gpg_strerror (err)); (unsigned int)erroff, gpg_strerror (err));
return err; else
}
*result = s_skey; *result = s_skey;
return 0; }
leave:
if (!err && r_keymeta)
*r_keymeta = pk;
else
nvc_release (pk);
if (ctrl->ephemeral_mode)
wipe_and_fclose (fp);
else
es_fclose (fp);
xfree (fname);
return err;
} }
@ -1226,7 +1384,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
if (!grip && !ctrl->have_keygrip) if (!grip && !ctrl->have_keygrip)
return gpg_error (GPG_ERR_NO_SECKEY); return gpg_error (GPG_ERR_NO_SECKEY);
err = read_key_file (grip? grip : ctrl->keygrip, &s_skey, &keymeta, NULL); err = read_key_file (ctrl, grip? grip : ctrl->keygrip,
&s_skey, &keymeta, NULL);
if (err) if (err)
{ {
if (gpg_err_code (err) == GPG_ERR_ENOENT) if (gpg_err_code (err) == GPG_ERR_ENOENT)
@ -1485,7 +1644,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
*result = NULL; *result = NULL;
err = read_key_file (grip, &s_skey, r_keymeta, NULL); err = read_key_file (ctrl, grip, &s_skey, r_keymeta, NULL);
if (!err) if (!err)
*result = s_skey; *result = s_skey;
return err; return err;
@ -1528,7 +1687,7 @@ public_key_from_file (ctrl_t ctrl, const unsigned char *grip,
if (r_sshorder) if (r_sshorder)
*r_sshorder = 0; *r_sshorder = 0;
err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL, NULL); err = read_key_file (ctrl, grip, &s_skey, for_ssh? &keymeta : NULL, NULL);
if (err) if (err)
return err; return err;
@ -1658,11 +1817,21 @@ agent_ssh_key_from_file (ctrl_t ctrl,
/* Check whether the secret key identified by GRIP is available. /* Check whether the secret key identified by GRIP is available.
Returns 0 is the key is available. */ Returns 0 is the key is available. */
int int
agent_key_available (const unsigned char *grip) agent_key_available (ctrl_t ctrl, const unsigned char *grip)
{ {
int result; int result;
char *fname; char *fname;
char hexgrip[40+4+1]; char hexgrip[40+4+1];
ephemeral_private_key_t ek;
if (ctrl && ctrl->ephemeral_mode)
{
for (ek = ctrl->ephemeral_keys; ek; ek = ek->next)
if (!memcmp (ek->grip, grip, KEYGRIP_LEN)
&& ek->keybuf && ek->keybuflen)
return 0;
return -1;
}
bin2hex (grip, 20, hexgrip); bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key"); strcpy (hexgrip+40, ".key");
@ -1675,7 +1844,6 @@ agent_key_available (const unsigned char *grip)
} }
/* Return the information about the secret key specified by the binary /* Return the information about the secret key specified by the binary
keygrip GRIP. If the key is a shadowed one the shadow information keygrip GRIP. If the key is a shadowed one the shadow information
will be stored at the address R_SHADOW_INFO as an allocated will be stored at the address R_SHADOW_INFO as an allocated
@ -1700,7 +1868,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
{ {
gcry_sexp_t sexp; gcry_sexp_t sexp;
err = read_key_file (grip, &sexp, NULL, NULL); err = read_key_file (ctrl, grip, &sexp, NULL, NULL);
if (err) if (err)
{ {
if (gpg_err_code (err) == GPG_ERR_ENOENT) if (gpg_err_code (err) == GPG_ERR_ENOENT)
@ -1784,7 +1952,13 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
char *default_desc = NULL; char *default_desc = NULL;
int key_type; int key_type;
err = read_key_file (grip, &s_skey, NULL, NULL); if (ctrl->ephemeral_mode)
{
err = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
err = read_key_file (ctrl, grip, &s_skey, NULL, NULL);
if (gpg_err_code (err) == GPG_ERR_ENOENT) if (gpg_err_code (err) == GPG_ERR_ENOENT)
err = gpg_error (GPG_ERR_NO_SECKEY); err = gpg_error (GPG_ERR_NO_SECKEY);
if (err) if (err)
@ -1885,7 +2059,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
card's SERIALNO and the IDSTRING. With FORCE passed as true an card's SERIALNO and the IDSTRING. With FORCE passed as true an
existing key with the given GRIP will get overwritten. */ existing key with the given GRIP will get overwritten. */
gpg_error_t gpg_error_t
agent_write_shadow_key (const unsigned char *grip, agent_write_shadow_key (ctrl_t ctrl, const unsigned char *grip,
const char *serialno, const char *keyid, const char *serialno, const char *keyid,
const unsigned char *pkbuf, int force, const unsigned char *pkbuf, int force,
const char *dispserialno) const char *dispserialno)
@ -1915,7 +2089,7 @@ agent_write_shadow_key (const unsigned char *grip,
} }
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
err = agent_write_private_key (grip, shdkey, len, force, err = agent_write_private_key (ctrl, grip, shdkey, len, force,
serialno, keyid, dispserialno, 0); serialno, keyid, dispserialno, 0);
xfree (shdkey); xfree (shdkey);
if (err) if (err)

View File

@ -30,14 +30,36 @@
#include "../common/exechelp.h" #include "../common/exechelp.h"
#include "../common/sysutils.h" #include "../common/sysutils.h"
static int
store_key (gcry_sexp_t private, const char *passphrase, int force, void
clear_ephemeral_keys (ctrl_t ctrl)
{
while (ctrl->ephemeral_keys)
{
ephemeral_private_key_t next = ctrl->ephemeral_keys->next;
if (ctrl->ephemeral_keys->keybuf)
{
wipememory (ctrl->ephemeral_keys->keybuf,
ctrl->ephemeral_keys->keybuflen);
xfree (ctrl->ephemeral_keys->keybuf);
}
xfree (ctrl->ephemeral_keys);
ctrl->ephemeral_keys = next;
}
}
/* Store the key either to a file, or in ctrl->ephemeral_mode in the
* session data. */
static gpg_error_t
store_key (ctrl_t ctrl, gcry_sexp_t private,
const char *passphrase, int force,
unsigned long s2k_count, time_t timestamp) unsigned long s2k_count, time_t timestamp)
{ {
int rc; gpg_error_t err;
unsigned char *buf; unsigned char *buf;
size_t len; size_t len;
unsigned char grip[20]; unsigned char grip[KEYGRIP_LEN];
if ( !gcry_pk_get_keygrip (private, grip) ) if ( !gcry_pk_get_keygrip (private, grip) )
{ {
@ -49,7 +71,10 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
log_assert (len); log_assert (len);
buf = gcry_malloc_secure (len); buf = gcry_malloc_secure (len);
if (!buf) if (!buf)
return out_of_core (); {
err = gpg_error_from_syserror ();
goto leave;
}
len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
log_assert (len); log_assert (len);
@ -57,20 +82,56 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
{ {
unsigned char *p; unsigned char *p;
rc = agent_protect (buf, passphrase, &p, &len, s2k_count); err = agent_protect (buf, passphrase, &p, &len, s2k_count);
if (rc) if (err)
{ goto leave;
xfree (buf);
return rc;
}
xfree (buf); xfree (buf);
buf = p; buf = p;
} }
rc = agent_write_private_key (grip, buf, len, force, if (ctrl->ephemeral_mode)
{
ephemeral_private_key_t ek;
for (ek = ctrl->ephemeral_keys; ek; ek = ek->next)
if (!memcmp (ek->grip, grip, KEYGRIP_LEN))
break;
if (!ek)
{
ek = xtrycalloc (1, sizeof *ek);
if (!ek)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (ek->grip, grip, KEYGRIP_LEN);
ek->next = ctrl->ephemeral_keys;
ctrl->ephemeral_keys = ek;
}
if (ek->keybuf)
{
wipememory (ek->keybuf, ek->keybuflen);
xfree (ek->keybuf);
}
ek->keybuf = buf;
buf = NULL;
ek->keybuflen = len;
}
else
err = agent_write_private_key (ctrl, grip, buf, len, force,
NULL, NULL, NULL, timestamp); NULL, NULL, NULL, timestamp);
if (!err)
{
char hexgrip[2*KEYGRIP_LEN+1];
bin2hex (grip, KEYGRIP_LEN, hexgrip);
agent_write_status (ctrl, "KEYGRIP", hexgrip, NULL);
}
leave:
xfree (buf); xfree (buf);
return rc; return err;
} }
@ -450,16 +511,19 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
/* Generate a new keypair according to the parameters given in /* Generate a new keypair according to the parameters given in
KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase * KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase
using the cache nonce. If NO_PROTECTION is true the key will not * using the cache nonce. If NO_PROTECTION is true the key will not
be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that * be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that
passphrase will be used for the new key. If TIMESTAMP is not zero * passphrase will be used for the new key. If TIMESTAMP is not zero
it will be recorded as creation date of the key (unless extended * it will be recorded as creation date of the key (unless extended
format is disabled) . */ * format is disabled). In ctrl_ephemeral_mode the key is stored in
* the session data and an identifier is returned using a status
* line. */
int int
agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp, agent_genkey (ctrl_t ctrl, unsigned int flags,
const char *keyparam, size_t keyparamlen, int no_protection, const char *cache_nonce, time_t timestamp,
const char *override_passphrase, int preset, membuf_t *outbuf) const char *keyparam, size_t keyparamlen,
const char *override_passphrase, membuf_t *outbuf)
{ {
gcry_sexp_t s_keyparam, s_key, s_private, s_public; gcry_sexp_t s_keyparam, s_key, s_private, s_public;
char *passphrase_buffer = NULL; char *passphrase_buffer = NULL;
@ -478,7 +542,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp,
/* Get the passphrase now, cause key generation may take a while. */ /* Get the passphrase now, cause key generation may take a while. */
if (override_passphrase) if (override_passphrase)
passphrase = override_passphrase; passphrase = override_passphrase;
else if (no_protection || !cache_nonce) else if ((flags & GENKEY_FLAG_NO_PROTECTION) || !cache_nonce)
passphrase = NULL; passphrase = NULL;
else else
{ {
@ -486,8 +550,8 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp,
passphrase = passphrase_buffer; passphrase = passphrase_buffer;
} }
if (passphrase || no_protection) if (passphrase || (flags & GENKEY_FLAG_NO_PROTECTION))
; ; /* No need to ask for a passphrase. */
else else
{ {
rc = agent_ask_new_passphrase (ctrl, rc = agent_ask_new_passphrase (ctrl,
@ -532,11 +596,14 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp,
gcry_sexp_release (s_key); s_key = NULL; gcry_sexp_release (s_key); s_key = NULL;
/* store the secret key */ /* store the secret key */
if (DBG_CRYPTO) if (opt.verbose)
log_debug ("storing private key\n"); log_info ("storing %sprivate key\n",
rc = store_key (s_private, passphrase, 0, ctrl->s2k_count, timestamp); ctrl->ephemeral_mode?"ephemeral ":"");
if (!rc) rc = store_key (ctrl, s_private, passphrase, 0, ctrl->s2k_count, timestamp);
if (!rc && !ctrl->ephemeral_mode)
{ {
/* FIXME: or does it make sense to also cache passphrases in
* ephemeral mode using a dedicated cache? */
if (!cache_nonce) if (!cache_nonce)
{ {
char tmpbuf[12]; char tmpbuf[12];
@ -544,18 +611,20 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp,
cache_nonce = bin2hex (tmpbuf, 12, NULL); cache_nonce = bin2hex (tmpbuf, 12, NULL);
} }
if (cache_nonce if (cache_nonce
&& !no_protection && !(flags & GENKEY_FLAG_NO_PROTECTION)
&& !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE, && !agent_put_cache (ctrl, cache_nonce, CACHE_MODE_NONCE,
passphrase, ctrl->cache_ttl_opt_preset)) passphrase, ctrl->cache_ttl_opt_preset))
agent_write_status (ctrl, "CACHE_NONCE", cache_nonce, NULL); agent_write_status (ctrl, "CACHE_NONCE", cache_nonce, NULL);
if (preset && !no_protection) if ((flags & GENKEY_FLAG_PRESET)
&& !(flags & GENKEY_FLAG_NO_PROTECTION))
{ {
unsigned char grip[20]; unsigned char grip[20];
char hexgrip[40+1]; char hexgrip[40+1];
if (gcry_pk_get_keygrip (s_private, grip)) if (gcry_pk_get_keygrip (s_private, grip))
{ {
bin2hex(grip, 20, hexgrip); bin2hex(grip, 20, hexgrip);
rc = agent_put_cache (ctrl, hexgrip, CACHE_MODE_ANY, passphrase, rc = agent_put_cache (ctrl, hexgrip,
CACHE_MODE_ANY, passphrase,
ctrl->cache_ttl_opt_preset); ctrl->cache_ttl_opt_preset);
} }
} }
@ -607,7 +676,8 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
if (passphrase_addr && *passphrase_addr) if (passphrase_addr && *passphrase_addr)
{ {
/* Take an empty string as request not to protect the key. */ /* Take an empty string as request not to protect the key. */
err = store_key (s_skey, **passphrase_addr? *passphrase_addr:NULL, 1, err = store_key (ctrl, s_skey,
**passphrase_addr? *passphrase_addr:NULL, 1,
ctrl->s2k_count, 0); ctrl->s2k_count, 0);
} }
else else
@ -623,7 +693,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
L_("Please enter the new passphrase"), L_("Please enter the new passphrase"),
&pass); &pass);
if (!err) if (!err)
err = store_key (s_skey, pass, 1, ctrl->s2k_count, 0); err = store_key (ctrl, s_skey, pass, 1, ctrl->s2k_count, 0);
if (!err && passphrase_addr) if (!err && passphrase_addr)
*passphrase_addr = pass; *passphrase_addr = pass;
else else

View File

@ -1989,6 +1989,7 @@ agent_deinit_default_ctrl (ctrl_t ctrl)
{ {
unregister_progress_cb (); unregister_progress_cb ();
session_env_release (ctrl->session_env); session_env_release (ctrl->session_env);
clear_ephemeral_keys (ctrl);
xfree (ctrl->digest.data); xfree (ctrl->digest.data);
ctrl->digest.data = NULL; ctrl->digest.data = NULL;

View File

@ -397,7 +397,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
for (p=item->hexgrip, i=0; i < 20; p += 2, i++) for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
grip[i] = xtoi_2 (p); grip[i] = xtoi_2 (p);
if (!force && !agent_key_available (grip)) if (!force && !agent_key_available (ctrl, grip))
continue; /* The key is already available. */ continue; /* The key is already available. */
/* Unknown key - store it. */ /* Unknown key - store it. */
@ -408,12 +408,14 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
goto leave; goto leave;
} }
if (!ctrl->ephemeral_mode)
{ {
char *dispserialno; char *dispserialno;
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno, agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno,
item->hexgrip); item->hexgrip);
rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force, rc = agent_write_shadow_key (ctrl,
grip, serialno, item->id, pubkey, force,
dispserialno); dispserialno);
xfree (dispserialno); xfree (dispserialno);
} }

View File

@ -371,13 +371,14 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
goto leave; goto leave;
} }
if (keyref) if (keyref && !ctrl->ephemeral_mode)
{ {
char *dispserialno; char *dispserialno;
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno, agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno,
hexgrip); hexgrip);
agent_write_shadow_key (ctrl->keygrip, serialno, keyref, pkbuf, agent_write_shadow_key (ctrl,
ctrl->keygrip, serialno, keyref, pkbuf,
0, dispserialno); 0, dispserialno);
xfree (dispserialno); xfree (dispserialno);
} }

View File

@ -755,8 +755,9 @@ release_passphrase (char *pw)
/* Stub function. */ /* Stub function. */
int int
agent_key_available (const unsigned char *grip) agent_key_available (ctrl_t ctrl, const unsigned char *grip)
{ {
(void)ctrl;
(void)grip; (void)grip;
return -1; /* Not available. */ return -1; /* Not available. */
} }
@ -814,7 +815,7 @@ agent_askpin (ctrl_t ctrl,
/* Replacement for the function in findkey.c. Here we write the key /* Replacement for the function in findkey.c. Here we write the key
* to stdout. */ * to stdout. */
gpg_error_t gpg_error_t
agent_write_private_key (const unsigned char *grip, agent_write_private_key (ctrl_t ctrl, const unsigned char *grip,
const void *buffer, size_t length, int force, const void *buffer, size_t length, int force,
const char *serialno, const char *keyref, const char *serialno, const char *keyref,
const char *dispserialno, time_t timestamp) const char *dispserialno, time_t timestamp)
@ -822,6 +823,7 @@ agent_write_private_key (const unsigned char *grip,
char hexgrip[40+4+1]; char hexgrip[40+4+1];
char *p; char *p;
(void)ctrl;
(void)force; (void)force;
(void)serialno; (void)serialno;
(void)keyref; (void)keyref;