diff options
Diffstat (limited to 'agent/findkey.c')
-rw-r--r-- | agent/findkey.c | 245 |
1 files changed, 108 insertions, 137 deletions
diff --git a/agent/findkey.c b/agent/findkey.c index 060cb786d..098d5224f 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -2,6 +2,7 @@ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2019 Werner Koch + * Copyright (C) 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -79,19 +80,114 @@ linefeed_to_percent0A (const char *string) } -/* Note: Ownership of FNAME and FP are moved to this function. */ -static gpg_error_t -write_extended_private_key (char *fname, estream_t fp, int update, int newkey, - const void *buf, size_t len, - const char *serialno, const char *keyref, - time_t timestamp) +/* Write the S-expression formatted key (BUFFER,LENGTH) to our key + * storage. With FORCE passed as true an existing key with the given + * GRIP will get overwritten. If SERIALNO and KEYREF are given a + * Token line is added to the key if the extended format is used. If + * TIMESTAMP is not zero and the key doies not yet exists it will be + * recorded as creation date. */ +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force, + const char *serialno, const char *keyref, + time_t timestamp) { gpg_error_t err; + char *fname; + estream_t fp; + char hexgrip[40+4+1]; + int update, newkey; nvc_t pk = NULL; gcry_sexp_t key = NULL; int remove = 0; char *token = NULL; + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + + /* FIXME: Write to a temp file first so that write failures during + key updates won't lead to a key loss. */ + + if (!force && !gnupg_access (fname, F_OK)) + { + log_error ("secret key file '%s' already exists\n", fname); + xfree (fname); + return gpg_error (GPG_ERR_EEXIST); + } + + fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); + if (!fp) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + + if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) + { + fp = es_fopen (fname, "wbx,mode=-rw"); + if (!fp) + tmperr = gpg_error_from_syserror (); + } + if (!fp) + { + log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); + xfree (fname); + return tmperr; + } + update = 0; + newkey = 1; + } + else if (force) + { + gpg_error_t rc; + char first; + + /* See if an existing key is in extended format. */ + 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 still in the old format - force it into extended + * format. We do not request an update here because an + * existing key is not yet in extended key format and no + * extended infos are yet available. */ + update = 0; + newkey = 0; + } + else + { + /* Key is already in the extended format. */ + update = 1; + newkey = 0; + } + } + else + { + /* The key file did not exist: we assume this is a new key and + * write the Created: entry. */ + update = 0; + newkey = 1; + } + + if (update) { int line; @@ -115,10 +211,11 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, } es_clearerr (fp); - err = gcry_sexp_sscan (&key, NULL, buf, len); + /* Turn (BUFFER,LENGTH) into a gcrypt s-expression and set it into + * our name value container. */ + err = gcry_sexp_sscan (&key, NULL, buffer, length); if (err) goto leave; - err = nvc_set_private_key (pk, key); if (err) goto leave; @@ -153,7 +250,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, } } - /* If a timestamp has been supplied and the key is new write a + /* If a timestamp has been supplied and the key is new, write a * creation timestamp. (We douple check that there is no Created * item yet.)*/ if (timestamp && newkey && !nvc_lookup (pk, "Created:")) @@ -166,7 +263,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, goto leave; } - + /* Back to start and write. */ err = es_fseek (fp, 0, SEEK_SET); if (err) goto leave; @@ -212,133 +309,6 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey, 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. If SERIALNO and KEYREF are given a Token line is - * added to the key if the extended format is used. If TIMESTAMP is - * not zero and the key doies not yet exists it will be recorded as - * creation date. */ -int -agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force, - const char *serialno, const char *keyref, - time_t timestamp) -{ - char *fname; - estream_t fp; - char hexgrip[40+4+1]; - - bin2hex (grip, 20, hexgrip); - strcpy (hexgrip+40, ".key"); - - fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, - hexgrip, NULL); - - /* FIXME: Write to a temp file first so that write failures during - key updates won't lead to a key loss. */ - - if (!force && !gnupg_access (fname, F_OK)) - { - log_error ("secret key file '%s' already exists\n", fname); - xfree (fname); - return gpg_error (GPG_ERR_EEXIST); - } - - fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); - if (!fp) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - - if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) - { - fp = es_fopen (fname, "wbx,mode=-rw"); - if (!fp) - tmperr = gpg_error_from_syserror (); - } - if (!fp) - { - log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); - xfree (fname); - return tmperr; - } - } - else if (force) - { - gpg_error_t rc; - char first; - - /* See if an existing key is in extended format. */ - 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 already in the extended format. */ - return write_extended_private_key (fname, fp, 1, 0, buffer, length, - serialno, keyref, timestamp); - } - if (first == '(' && opt.enable_extended_key_format) - { - /* Key is in the old format - but we want the extended format. */ - return write_extended_private_key (fname, fp, 0, 0, buffer, length, - serialno, keyref, timestamp); - } - } - - if (opt.enable_extended_key_format) - return write_extended_private_key (fname, fp, 0, 1, buffer, length, - serialno, keyref, timestamp); - - if (es_fwrite (buffer, length, 1, fp) != 1) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); - es_fclose (fp); - gnupg_remove (fname); - 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 (); - log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - bump_key_eventcounter (); - xfree (fname); - return 0; -} - gpg_error_t agent_update_private_key (const unsigned char *grip, nvc_t pk) @@ -393,6 +363,7 @@ agent_update_private_key (const unsigned char *grip, nvc_t pk) return err; } + /* Callback function to try the unprotection from the passphrase query code. */ static gpg_error_t |