diff options
Diffstat (limited to 'agent/findkey.c')
-rw-r--r-- | agent/findkey.c | 161 |
1 files changed, 160 insertions, 1 deletions
diff --git a/agent/findkey.c b/agent/findkey.c index 3cf8d0cc1..a78709cc2 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -35,6 +35,7 @@ #include "agent.h" #include "i18n.h" #include "../common/ssh-utils.h" +#include "../common/private-keys.h" #ifndef O_BINARY #define O_BINARY 0 @@ -51,6 +52,75 @@ struct try_unprotect_arg_s }; +static gpg_error_t +write_extended_private_key (char *fname, estream_t fp, + const void *buf, size_t len) +{ + gpg_error_t err; + pkc_t pk = NULL; + gcry_sexp_t key = NULL; + int remove = 0; + int line; + + err = pkc_parse (&pk, &line, fp); + if (err) + { + log_error ("error parsing '%s' line %d: %s\n", + fname, line, gpg_strerror (err)); + goto leave; + } + + err = gcry_sexp_sscan (&key, NULL, buf, len); + if (err) + goto leave; + + err = pkc_set_private_key (pk, key); + if (err) + goto leave; + + err = es_fseek (fp, 0, SEEK_SET); + if (err) + goto leave; + + err = pkc_write (pk, fp); + if (err) + { + log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + + if (ftruncate (es_fileno (fp), es_ftello (fp))) + { + err = gpg_error_from_syserror (); + log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + + if (es_fclose (fp)) + { + err = gpg_error_from_syserror (); + log_error ("error closing '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + else + fp = NULL; + + bump_key_eventcounter (); + + leave: + if (fp) + es_fclose (fp); + if (remove) + gnupg_remove (fname); + xfree (fname); + gcry_sexp_release (key); + pkc_release (pk); + return err; +} + /* Write an S-expression formatted key to our key storage. With FORCE passed as true an existing key with the given GRIP will get overwritten. */ @@ -77,7 +147,7 @@ agent_write_private_key (const unsigned char *grip, return gpg_error (GPG_ERR_EEXIST); } - fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw"); + fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); @@ -86,6 +156,38 @@ agent_write_private_key (const unsigned char *grip, return tmperr; } + /* See if an existing key is in extended format. */ + if (force) + { + gpg_error_t rc; + char first; + + if (es_fread (&first, 1, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading first byte from '%s': %s\n", + fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + rc = es_fseek (fp, 0, SEEK_SET); + if (rc) + { + log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + if (first != '(') + { + /* Key is in extended format. */ + return write_extended_private_key (fname, fp, buffer, length); + } + } + if (es_fwrite (buffer, length, 1, fp) != 1) { gpg_error_t tmperr = gpg_error_from_syserror (); @@ -95,6 +197,18 @@ agent_write_private_key (const unsigned char *grip, xfree (fname); return tmperr; } + + /* When force is given, the file might have to be truncated. */ + if (force && ftruncate (es_fileno (fp), es_ftello (fp))) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr)); + es_fclose (fp); + gnupg_remove (fname); + xfree (fname); + return tmperr; + } + if (es_fclose (fp)) { gpg_error_t tmperr = gpg_error_from_syserror (); @@ -531,6 +645,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; + char first; *result = NULL; @@ -548,6 +663,50 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) return rc; } + if (es_fread (&first, 1, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading first byte from '%s': %s\n", + fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + rc = es_fseek (fp, 0, SEEK_SET); + if (rc) + { + log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + if (first != '(') + { + /* Key is in extended format. */ + pkc_t pk; + int line; + + rc = pkc_parse (&pk, &line, fp); + es_fclose (fp); + + if (rc) + log_error ("error parsing '%s' line %d: %s\n", + fname, line, gpg_strerror (rc)); + else + { + rc = pkc_get_private_key (pk, result); + pkc_release (pk); + if (rc) + log_error ("error getting private key from '%s': %s\n", + fname, gpg_strerror (rc)); + } + + xfree (fname); + return rc; + } + if (fstat (es_fileno (fp), &st)) { rc = gpg_error_from_syserror (); |