diff --git a/agent/agent.h b/agent/agent.h index d33b8cd34..30f30200d 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -37,6 +37,7 @@ #include "../common/sysutils.h" /* (gnupg_fd_t) */ #include "../common/session-env.h" #include "../common/shareddefs.h" +#include "../common/name-value.h" /* To convey some special hash algorithms we use algorithm numbers reserved for application use. */ @@ -471,7 +472,7 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, gcry_sexp_t *result, char **r_passphrase, time_t *r_timestamp); gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, - gcry_sexp_t *result); + gcry_sexp_t *result, nvc_t *r_keymeta); gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); @@ -488,6 +489,7 @@ 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, const unsigned char *grip, int force, int only_stubs); +gpg_error_t agent_update_private_key (const unsigned char *grip, nvc_t pk); /*-- call-pinentry.c --*/ void initialize_module_call_pinentry (void); diff --git a/agent/command-ssh.c b/agent/command-ssh.c index b52604b70..2c18796bc 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2760,7 +2760,7 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec, char *fpr, *prompt; char *comment = NULL; - err = agent_raw_key_from_file (ctrl, ctrl->keygrip, &key); + err = agent_raw_key_from_file (ctrl, ctrl->keygrip, &key, NULL); if (err) goto out; err = ssh_get_fingerprint_string (key, opt.ssh_fingerprint_digest, &fpr); diff --git a/agent/command.c b/agent/command.c index ead026341..50196f432 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1200,7 +1200,84 @@ cmd_genkey (assuan_context_t ctx, char *line) } +static const char hlp_keyattr[] = + "KEYATTR [--delete] []\n" + "\n" + "For the secret key, show the attribute of ATTRNAME. With VALUE,\n" + "put the value to the attribute. Use --delete option to delete."; +static gpg_error_t +cmd_keyattr (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + const char *argv[3]; + int argc; + unsigned char grip[20]; + int opt_delete; + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + opt_delete = has_option (line, "--delete"); + + line = skip_options (line); + + argc = split_fields (line, argv, DIM (argv)); + if (argc < 2) + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + + err = parse_keygrip (ctx, argv[0], grip); + if (err) + goto leave; + + if (!err) + { + gcry_sexp_t s_key = NULL; + nvc_t keymeta = NULL; + const char *p; + + err = agent_raw_key_from_file (ctrl, grip, &s_key, &keymeta); + if (keymeta == NULL) /* Not extended format? */ + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + if (argc == 2) + { + nve_t e = nvc_lookup (keymeta, argv[1]); + + if (opt_delete) + { + if (e) + nvc_delete (keymeta, e); + } + else if (e) + { + p = nve_value (e); + if (p) + err = assuan_send_data (ctx, p, strlen (p)); + } + } + else if (argc == 3) + { + err = nvc_set (keymeta, argv[1], argv[2]); + if (!err) + err = nvc_set_private_key (keymeta, s_key); + if (!err) + err = agent_update_private_key (grip, keymeta); + } + + nvc_release (keymeta); + gcry_sexp_release (s_key); + } + + leave: + return leave_cmd (ctx, err); +} static const char hlp_readkey[] = "READKEY [--no-data] [--format=ssh] \n" @@ -1461,7 +1538,7 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, { gcry_sexp_t key; - if (!agent_raw_key_from_file (ctrl, grip, &key)) + if (!agent_raw_key_from_file (ctrl, grip, &key, NULL)) { ssh_get_fingerprint_string (key, with_ssh_fpr, &fpr); gcry_sexp_release (key); @@ -4044,7 +4121,8 @@ register_commands (assuan_context_t ctx) { "RELOADAGENT", cmd_reloadagent,hlp_reloadagent }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "KEYTOCARD", cmd_keytocard, hlp_keytocard }, - { "KEYTOTPM", cmd_keytotpm, hlp_keytotpm }, + { "KEYTOTPM", cmd_keytotpm, hlp_keytotpm }, + { "KEYATTR", cmd_keyattr, hlp_keyattr }, { NULL } }; int i, rc; diff --git a/agent/findkey.c b/agent/findkey.c index 22ce20a86..8c128ce00 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -33,7 +33,6 @@ #include "agent.h" #include "../common/i18n.h" #include "../common/ssh-utils.h" -#include "../common/name-value.h" #ifndef O_BINARY #define O_BINARY 0 @@ -339,6 +338,59 @@ agent_write_private_key (const unsigned char *grip, } +gpg_error_t +agent_update_private_key (const unsigned char *grip, nvc_t pk) +{ + char *fname, *fname0; + estream_t fp; + char hexgrip[40+8+1]; + gpg_error_t err; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key.tmp"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + fname0 = xstrdup (fname); + if (!fname0) + { + err = gpg_error_from_syserror (); + xfree (fname); + return err; + } + fname0[strlen (fname)-4] = 0; + + fp = es_fopen (fname, "wbx,mode=-rw"); + if (!fp) + { + err = gpg_error_from_syserror (); + + log_error ("can't create '%s': %s\n", fname, gpg_strerror (err)); + xfree (fname); + return err; + } + + err = nvc_write (pk, fp); + if (err) + log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); + + es_fclose (fp); + +#ifdef HAVE_W32_SYSTEM + /* No atomic mv on W32 systems. */ + gnupg_remove (fname0); +#endif + if (rename (fname, fname0)) + { + err = gpg_error_from_errno (errno); + log_error (_("error renaming '%s' to '%s': %s\n"), + fname, fname0, strerror (errno)); + } + + xfree (fname); + return err; +} + /* Callback function to try the unprotection from the passphrase query code. */ static gpg_error_t @@ -1349,7 +1401,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, failure an error code is returned and NULL stored at RESULT. */ gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, - gcry_sexp_t *result) + gcry_sexp_t *result, nvc_t *r_keymeta) { gpg_error_t err; gcry_sexp_t s_skey; @@ -1358,7 +1410,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, *result = NULL; - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, r_keymeta); if (!err) *result = s_skey; return err;