1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-24 15:17:02 +01:00

agent: Let READKEY update the display-s/n of the Token entry.

* agent/findkey.c (agent_write_private_key): Factor file name
generation out to ...
(fname_from_keygrip): new.
(write_extended_private_key): Add and implement new arg MAYBE_UPDATE.
(agent_write_shadow_key): Ditto.

* agent/command.c (cmd_readkey): Update the shadow-key in card mode.
--

GnuPG-bug-id 6135
This commit is contained in:
Werner Koch 2022-08-16 12:02:51 +02:00
parent 8e393e2592
commit 755920d433
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 176 additions and 66 deletions

View File

@ -552,7 +552,8 @@ 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 (int maybe_update,
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

@ -2499,7 +2499,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
/* (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);
err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0, err = agent_write_shadow_key (0, grip, serialno, authkeyid, pkbuf, 0,
dispserialno); dispserialno);
xfree (dispserialno); xfree (dispserialno);
if (err) if (err)

View File

@ -995,6 +995,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
char *keyidbuf = NULL; char *keyidbuf = NULL;
size_t pkbuflen; size_t pkbuflen;
int opt_card, opt_no_data; int opt_card, opt_no_data;
char *dispserialno = NULL;
if (ctrl->restricted) if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
@ -1037,18 +1038,23 @@ cmd_readkey (assuan_context_t ctx, char *line)
goto leave; goto leave;
} }
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno);
if (agent_key_available (grip)) if (agent_key_available (grip))
{ {
/* (Shadow)-key is not available in our key storage. */ /* Shadow-key is not available in our key storage. */
char *dispserialno; rc = agent_write_shadow_key (0, grip, serialno, keyid, pkbuf, 0,
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno);
rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0,
dispserialno); dispserialno);
xfree (dispserialno); }
else
{
/* Shadow-key is available in our key storage but ne check
* whether we need to update it with a new display-s/n or
* whatever. */
rc = agent_write_shadow_key (1, grip, serialno, keyid, pkbuf, 0,
dispserialno);
}
if (rc) if (rc)
goto leave; goto leave;
}
rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen); rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen);
} }
@ -1079,6 +1085,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
xfree (keyidbuf); xfree (keyidbuf);
xfree (serialno); xfree (serialno);
xfree (pkbuf); xfree (pkbuf);
xfree (dispserialno);
gcry_sexp_release (s_pkey); gcry_sexp_release (s_pkey);
return leave_cmd (ctx, rc); return leave_cmd (ctx, rc);
} }

View File

@ -52,9 +52,30 @@ struct try_unprotect_arg_s
}; };
/* Note: Ownership of FNAME and FP are moved to this function. */ /* Return the file name for the 20 byte keygrip GRIP. Return NULL on
* error. */
static char *
fname_from_keygrip (const unsigned char *grip)
{
char hexgrip[40+4+1];
bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
return make_filename_try (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR,
hexgrip, NULL);
}
/* Note: Ownership of FNAME and FP are moved to this function.
* OLD_FORMAT is true if the file exists but is still in the
* non-extended mode format. If MAYBE_UPDATE is set the function
* assumes that the file exists but writes it only if it figures that
* an update is required. */
static gpg_error_t static gpg_error_t
write_extended_private_key (char *fname, estream_t fp, int update, int newkey, write_extended_private_key (int maybe_update,
char *fname, estream_t fp,
int old_format, int newkey,
const void *buf, size_t len, time_t timestamp, const void *buf, size_t len, time_t timestamp,
const char *serialno, const char *keyref, const char *serialno, const char *keyref,
const char *dispserialno) const char *dispserialno)
@ -65,27 +86,32 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
int remove = 0; int remove = 0;
char *token0 = NULL; char *token0 = NULL;
char *token = NULL; char *token = NULL;
char *dispserialno_buffer = NULL;
char **tokenfields = NULL;
if (update) if (old_format || newkey)
{
int line;
err = nvc_parse_private_key (&pk, &line, fp);
if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
{
log_error ("error parsing '%s' line %d: %s\n",
fname, line, gpg_strerror (err));
goto leave;
}
}
else
{ {
/* We must create a new NVC if the key is still in the old
* format and of course if it is a new key. */
pk = nvc_new_private_key (); pk = nvc_new_private_key ();
if (!pk) if (!pk)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
maybe_update = 0; /* Always write. */
}
else
{ /* Parse the existing NVC. */
int lineno = 0;
err = nvc_parse_private_key (&pk, &lineno, fp);
if (err)
{
log_error ("error parsing '%s' line %d: %s\n",
fname, lineno, gpg_strerror (err));
goto leave;
}
} }
es_clearerr (fp); es_clearerr (fp);
@ -98,8 +124,9 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
goto leave; goto leave;
/* 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 double check that there is no Created * creation timestamp. Note that we can't add this item if we are
* item yet.)*/ * still in the old format. We also add an extra check that there
* is no Created item yet. */
if (timestamp && newkey && !nvc_lookup (pk, "Created:")) if (timestamp && newkey && !nvc_lookup (pk, "Created:"))
{ {
gnupg_isotime_t timebuf; gnupg_isotime_t timebuf;
@ -116,22 +143,22 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
nve_t item; nve_t item;
const char *s; const char *s;
size_t token0len; size_t token0len;
char *tmpdistsn = NULL;
if (dispserialno) if (dispserialno)
{ {
tmpdistsn = percent_plus_escape (dispserialno); /* Escape the DISPSERIALNO. */
if (!tmpdistsn) dispserialno_buffer = percent_plus_escape (dispserialno);
if (!dispserialno_buffer)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
dispserialno = dispserialno_buffer;
} }
token0 = strconcat (serialno, " ", keyref, NULL); token0 = strconcat (serialno, " ", keyref, NULL);
if (token0) if (token0)
token = strconcat (token0, " - ", tmpdistsn? tmpdistsn:"-", NULL); token = strconcat (token0, " - ", dispserialno? dispserialno:"-", NULL);
xfree (tmpdistsn);
if (!token0 || !token) if (!token0 || !token)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
@ -152,14 +179,24 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
err = nvc_add (pk, "Token:", token); err = nvc_add (pk, "Token:", token);
if (err) if (err)
goto leave; goto leave;
maybe_update = 0; /* Force write. */
} }
else else
{ {
/* Token exists: Update the display s/n. It may have /* Token exists: Update the display s/n. It may have
* changed due to changes in a newer software version. */ * changed due to changes in a newer software version. */
if (maybe_update && s && (tokenfields = strtokenize (s, " \t\n"))
&& tokenfields[0] && tokenfields[1] && tokenfields[2]
&& tokenfields[3]
&& !strcmp (tokenfields[3], dispserialno))
; /* No need to update Token entry. */
else
{
err = nve_set (item, token); err = nve_set (item, token);
if (err) if (err)
goto leave; goto leave;
maybe_update = 0; /* Force write. */
}
} }
} }
@ -167,6 +204,8 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
if (err) if (err)
goto leave; goto leave;
if (!maybe_update)
{
err = nvc_write (pk, fp); err = nvc_write (pk, fp);
if (!err) if (!err)
err = es_fflush (fp); err = es_fflush (fp);
@ -184,6 +223,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
remove = 1; remove = 1;
goto leave; goto leave;
} }
}
if (es_fclose (fp)) if (es_fclose (fp))
{ {
@ -195,6 +235,7 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
else else
fp = NULL; fp = NULL;
if (!maybe_update)
bump_key_eventcounter (); bump_key_eventcounter ();
leave: leave:
@ -204,6 +245,8 @@ write_extended_private_key (char *fname, estream_t fp, int update, int newkey,
xfree (fname); xfree (fname);
xfree (token); xfree (token);
xfree (token0); xfree (token0);
xfree (dispserialno_buffer);
xfree (tokenfields);
gcry_sexp_release (key); gcry_sexp_release (key);
nvc_release (pk); nvc_release (pk);
return err; return err;
@ -223,13 +266,10 @@ agent_write_private_key (const unsigned char *grip,
{ {
char *fname; char *fname;
estream_t fp; estream_t fp;
char hexgrip[40+4+1];
bin2hex (grip, 20, hexgrip); fname = fname_from_keygrip (grip);
strcpy (hexgrip+40, ".key"); if (fname)
return gpg_error_from_syserror ();
fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR,
hexgrip, NULL);
/* FIXME: Write to a temp file first so that write failures during /* FIXME: Write to a temp file first so that write failures during
key updates won't lead to a key loss. */ key updates won't lead to a key loss. */
@ -287,21 +327,24 @@ agent_write_private_key (const unsigned char *grip,
if (first != '(') if (first != '(')
{ {
/* Key is already in the extended format. */ /* Key is already in the extended format. */
return write_extended_private_key (fname, fp, 1, 0, buffer, length, return write_extended_private_key (0, fname, fp, 0, 0,
buffer, length,
timestamp, serialno, keyref, timestamp, serialno, keyref,
dispserialno); dispserialno);
} }
if (first == '(' && opt.enable_extended_key_format) if (first == '(' && opt.enable_extended_key_format)
{ {
/* Key is in the old format - but we want the extended format. */ /* Key is in the old format - but we want the extended format. */
return write_extended_private_key (fname, fp, 0, 0, buffer, length, return write_extended_private_key (0, fname, fp, 1, 0,
buffer, length,
timestamp, serialno, keyref, timestamp, serialno, keyref,
dispserialno); dispserialno);
} }
} }
if (opt.enable_extended_key_format) if (opt.enable_extended_key_format)
return write_extended_private_key (fname, fp, 0, 1, buffer, length, return write_extended_private_key (0, fname, fp, 0, 1,
buffer, length,
timestamp, serialno, keyref, timestamp, serialno, keyref,
dispserialno); dispserialno);
@ -1683,9 +1726,11 @@ 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. If existing key with the given GRIP will get overwritten. If
DISPSERIALNO is not NULL the human readable s/n will also be DISPSERIALNO is not NULL the human readable s/n will also be
recorded in the key file. */ recorded in the key file. If MAYBE_UPDATE is set it is assumed that
the shadow key already exists and we test whether we should update
it (FORCE is ignored in this case). */
gpg_error_t gpg_error_t
agent_write_shadow_key (const unsigned char *grip, agent_write_shadow_key (int maybe_update, 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)
@ -1694,6 +1739,12 @@ agent_write_shadow_key (const unsigned char *grip,
unsigned char *shadow_info; unsigned char *shadow_info;
unsigned char *shdkey; unsigned char *shdkey;
size_t len; size_t len;
char *fname = NULL;
estream_t fp = NULL;
char first;
if (maybe_update && !opt.enable_extended_key_format)
return 0; /* Silently ignore. */
/* Just in case some caller did not parse the stuff correctly, skip /* Just in case some caller did not parse the stuff correctly, skip
* leading spaces. */ * leading spaces. */
@ -1715,11 +1766,62 @@ 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);
if (maybe_update) /* Update mode. */
{
fname = fname_from_keygrip (grip);
if (!fname)
{
err = gpg_error_from_syserror ();
goto leave;
}
fp = es_fopen (fname, "rb+,mode=-rw");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("shadow key file '%s' disappeared\n", fname);
goto leave;
}
/* See if an existing key is in extended format. */
if (es_fread (&first, 1, 1, fp) != 1)
{
err = gpg_error_from_syserror ();
log_error ("error reading first byte from '%s': %s\n",
fname, gpg_strerror (err));
goto leave;
}
if (es_fseek (fp, 0, SEEK_SET))
{
err = gpg_error_from_syserror ();
log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err));
goto leave;
}
/* "(first == '(')" indicates that the key is in the old format. */
err = write_extended_private_key (maybe_update,
fname, fp, (first == '('), 0,
shdkey, len,
0, serialno, keyid,
dispserialno);
fname = NULL; /* Ownership was transferred. */
fp = NULL; /* Ditto. */
}
else /* Standard mode */
{
err = agent_write_private_key (grip, shdkey, len, force, 0, err = agent_write_private_key (grip, shdkey, len, force, 0,
serialno, keyid, dispserialno); serialno, keyid, dispserialno);
}
leave:
xfree (fname);
es_fclose (fp);
xfree (shdkey); xfree (shdkey);
if (err) if (err)
log_error ("error writing key: %s\n", gpg_strerror (err)); log_error ("error %s key: %s\n", maybe_update? "updating":"writing",
gpg_strerror (err));
return err; return err;
} }

View File

@ -413,8 +413,8 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
char *dispserialno; char *dispserialno;
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno); agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno);
rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force, rc = agent_write_shadow_key (0, grip, serialno, item->id, pubkey,
dispserialno); force, dispserialno);
xfree (dispserialno); xfree (dispserialno);
} }
xfree (pubkey); xfree (pubkey);