diff --git a/agent/agent.h b/agent/agent.h index 58e584132..4ed8c7fe6 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -334,6 +334,8 @@ int agent_key_available (const unsigned char *grip); gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, unsigned char **r_shadow_info); +gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip); /*-- call-pinentry.c --*/ void initialize_module_call_pinentry (void); diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 364a8ccb4..a81468125 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -1031,7 +1031,8 @@ search_control_file (ssh_control_file_t cf, const char *hexgrip, assert (strlen (hexgrip) == 40 ); - *r_disabled = 0; + if (r_disabled) + *r_disabled = 0; if (r_ttl) *r_ttl = 0; if (r_confirm) @@ -1047,7 +1048,8 @@ search_control_file (ssh_control_file_t cf, const char *hexgrip, } if (!err) { - *r_disabled = cf->item.disabled; + if (r_disabled) + *r_disabled = cf->item.disabled; if (r_ttl) *r_ttl = cf->item.ttl; if (r_confirm) diff --git a/agent/command.c b/agent/command.c index fab27f09d..52876a9a9 100644 --- a/agent/command.c +++ b/agent/command.c @@ -695,7 +695,7 @@ static const char hlp_setkeydesc[] = "blanks unless they are percent or '+' escaped.\n" "\n" "The description is only valid for the next PKSIGN, PKDECRYPT,\n" - "IMPORT_KEY or EXPORT_KEY operation."; + "IMPORT_KEY, EXPORT_KEY, or DELETE_KEY operation."; static gpg_error_t cmd_setkeydesc (assuan_context_t ctx, char *line) { @@ -2244,6 +2244,39 @@ cmd_export_key (assuan_context_t ctx, char *line) return leave_cmd (ctx, err); } + + + +static const char hlp_delete_key[] = + "DELETE_KEY \n" + "\n" + "Delete a secret key from the key store.\n" + "As safeguard the agent asks the user for confirmation.\n"; +static gpg_error_t +cmd_delete_key (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char grip[20]; + + line = skip_options (line); + + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + + err = agent_delete_key (ctrl, ctrl->server_local->keydesc, grip); + if (err) + goto leave; + + leave: + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + + return leave_cmd (ctx, err); +} + + static const char hlp_keytocard[] = "KEYTOCARD [--force] \n" @@ -2926,6 +2959,7 @@ register_commands (assuan_context_t ctx) { "KEYWRAP_KEY", cmd_keywrap_key, hlp_keywrap_key }, { "IMPORT_KEY", cmd_import_key, hlp_import_key }, { "EXPORT_KEY", cmd_export_key, hlp_export_key }, + { "DELETE_KEY", cmd_delete_key, hlp_delete_key }, { "GETVAL", cmd_getval, hlp_getval }, { "PUTVAL", cmd_putval, hlp_putval }, { "UPDATESTARTUPTTY", cmd_updatestartuptty, hlp_updatestartuptty }, diff --git a/agent/findkey.c b/agent/findkey.c index 84d2cfdc6..e01c5c195 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1,6 +1,7 @@ /* findkey.c - Locate the secret key * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * @@ -189,6 +190,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi) %% - Replaced by a single % %c - Replaced by the content of COMMENT. + %C - Same as %c but put into parentheses. %F - Replaced by an ssh style fingerprint computed from KEY. The functions returns 0 on success or an error code. On success a @@ -240,6 +242,20 @@ modify_description (const char *in, const char *comment, const gcry_sexp_t key, out_len += comment_length; break; + case 'C': /* Comment. */ + if (!comment_length) + ; + else if (out) + { + *out++ = '('; + memcpy (out, comment, comment_length); + out += comment_length; + *out++ = ')'; + } + else + out_len += comment_length + 2; + break; + case 'F': /* SSH style fingerprint. */ if (!ssh_fpr && key) ssh_get_fingerprint_string (key, &ssh_fpr); @@ -536,6 +552,24 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) } +/* Remove the key identified by GRIP from the private key directory. */ +static gpg_error_t +remove_key_file (const unsigned char *grip) +{ + gpg_error_t err = 0; + char *fname; + char hexgrip[40+4+1]; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); + if (gnupg_remove (fname)) + err = gpg_error_from_syserror (); + xfree (fname); + return err; +} + + /* Return the secret key as an S-Exp in RESULT after locating it using the GRIP. If the operation shall be diverted to a token, an allocated S-expression with the shadow_info part from the file is @@ -1145,3 +1179,112 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, xfree (buf); return err; } + + + +/* Delete the key with GRIP from the disk after having asked for + confirmation using DESC_TEXT. Common error codes are: + GPG_ERR_NO_SECKEY + GPG_ERR_KEY_ON_CARD + GPG_ERR_NOT_CONFIRMED +*/ +gpg_error_t +agent_delete_key (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip) +{ + gpg_error_t err; + gcry_sexp_t s_skey = NULL; + unsigned char *buf = NULL; + size_t len; + char *desc_text_final = NULL; + char *comment = NULL; + ssh_control_file_t cf = NULL; + char hexgrip[40+4+1]; + char *default_desc = NULL; + + err = read_key_file (grip, &s_skey); + if (gpg_err_code (err) == GPG_ERR_ENOENT) + err = gpg_error (GPG_ERR_NO_SECKEY); + if (err) + goto leave; + + err = make_canon_sexp (s_skey, &buf, &len); + if (err) + goto leave; + + switch (agent_private_key_type (buf)) + { + case PRIVATE_KEY_CLEAR: + case PRIVATE_KEY_PROTECTED: + { + bin2hex (grip, 20, hexgrip); + if (!desc_text) + { + default_desc = xtryasprintf + ("Do you really want to delete the key identified by keygrip%%0A" + " %s%%0A %%C%%0A?", hexgrip); + desc_text = default_desc; + } + + /* Note, that we will take the comment as a C string for + display purposes; i.e. all stuff beyond a Nul character is + ignored. */ + { + gcry_sexp_t comment_sexp; + + comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); + if (comment_sexp) + comment = gcry_sexp_nth_string (comment_sexp, 1); + gcry_sexp_release (comment_sexp); + } + + if (desc_text) + err = modify_description (desc_text, comment? comment:"", s_skey, + &desc_text_final); + if (err) + goto leave; + + err = agent_get_confirmation (ctrl, desc_text_final, + _("Delete key"), _("No"), 0); + if (err) + goto leave; + + cf = ssh_open_control_file (); + if (cf) + { + if (!ssh_search_control_file (cf, hexgrip, NULL, NULL, NULL)) + { + err = agent_get_confirmation + (ctrl, + _("Warning: This key is also listed for use with SSH!\n" + "Deleting the key will may remove your ability to" + "access remote machines."), + _("Delete key"), _("No"), 0); + if (err) + goto leave; + } + } + + err = remove_key_file (grip); + } + break; + + case PRIVATE_KEY_SHADOWED: + err = gpg_error (GPG_ERR_KEY_ON_CARD); + break; + + default: + log_error ("invalid private key format\n"); + err = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + + leave: + ssh_close_control_file (cf); + gcry_free (comment); + xfree (desc_text_final); + xfree (default_desc); + xfree (buf); + gcry_sexp_release (s_skey); + return err; +} diff --git a/configure.ac b/configure.ac index e384fba2d..096e3e170 100644 --- a/configure.ac +++ b/configure.ac @@ -44,7 +44,7 @@ m4_define([mym4_full_version],[mym4_version[]mym4_betastring]) AC_INIT([gnupg],[mym4_full_version], [http://bugs.gnupg.org]) -NEED_GPG_ERROR_VERSION=1.11 +NEED_GPG_ERROR_VERSION=1.13 NEED_LIBGCRYPT_API=1 NEED_LIBGCRYPT_VERSION=1.6.0