aboutsummaryrefslogtreecommitdiffstats
path: root/agent/findkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/findkey.c')
-rw-r--r--agent/findkey.c232
1 files changed, 188 insertions, 44 deletions
diff --git a/agent/findkey.c b/agent/findkey.c
index 89a18fa9e..370050d8b 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1,7 +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
+ * Copyright (C) 2014, 2019 Werner Koch
*
* This file is part of GnuPG.
*
@@ -26,10 +26,8 @@
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
-#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <assert.h>
#include <npth.h> /* (we use pth_sleep) */
#include "agent.h"
@@ -52,15 +50,47 @@ struct try_unprotect_arg_s
};
+/* Repalce all linefeeds in STRING by "%0A" and return a new malloced
+ * string. May return NULL on memory error. */
+static char *
+linefeed_to_percent0A (const char *string)
+{
+ const char *s;
+ size_t n;
+ char *buf, *p;
+
+ for (n=0, s=string; *s; s++)
+ if (*s == '\n')
+ n += 3;
+ else
+ n++;
+ p = buf = xtrymalloc (n+1);
+ if (!buf)
+ return NULL;
+ for (s=string; *s; s++)
+ if (*s == '\n')
+ {
+ memcpy (p, "%0A", 3);
+ p += 3;
+ }
+ else
+ *p++ = *s;
+ *p = 0;
+ return buf;
+}
+
+
/* 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,
- const void *buf, size_t len)
+ const void *buf, size_t len,
+ const char *serialno, const char *keyref)
{
gpg_error_t err;
nvc_t pk = NULL;
gcry_sexp_t key = NULL;
int remove = 0;
+ char *token = NULL;
if (update)
{
@@ -93,6 +123,37 @@ write_extended_private_key (char *fname, estream_t fp, int update,
if (err)
goto leave;
+ /* If requested write a Token line. */
+ if (serialno && keyref)
+ {
+ nve_t item;
+ const char *s;
+
+ token = strconcat (serialno, " ", keyref, NULL);
+ if (!token)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* fixme: the strcmp should compare only the first two strings. */
+ for (item = nvc_lookup (pk, "Token:");
+ item;
+ item = nve_next_value (item, "Token:"))
+ if ((s = nve_value (item)) && !strcmp (s, token))
+ break;
+ if (!item)
+ {
+ /* No token or no token with that value exists. Add a new
+ * one so that keys which have been stored on several cards
+ * are well supported. */
+ err = nvc_add (pk, "Token:", token);
+ if (err)
+ goto leave;
+ }
+ }
+
+
err = es_fseek (fp, 0, SEEK_SET);
if (err)
goto leave;
@@ -132,15 +193,18 @@ write_extended_private_key (char *fname, estream_t fp, int update,
xfree (fname);
gcry_sexp_release (key);
nvc_release (pk);
+ xfree (token);
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. */
+ * passed as true an existing key with the given GRIP will get
+ * overwritten. If SERIALNO and KEYREF are give an a Token line is added to
+ * th key if the extended format ist used. */
int
agent_write_private_key (const unsigned char *grip,
- const void *buffer, size_t length, int force)
+ const void *buffer, size_t length, int force,
+ const char *serialno, const char *keyref)
{
char *fname;
estream_t fp;
@@ -208,17 +272,20 @@ agent_write_private_key (const unsigned char *grip,
if (first != '(')
{
/* Key is already in the extended format. */
- return write_extended_private_key (fname, fp, 1, buffer, length);
+ return write_extended_private_key (fname, fp, 1, buffer, length,
+ serialno, keyref);
}
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, buffer, length);
+ return write_extended_private_key (fname, fp, 0, buffer, length,
+ serialno, keyref);
}
}
if (opt.enable_extended_key_format)
- return write_extended_private_key (fname, fp, 0, buffer, length);
+ return write_extended_private_key (fname, fp, 0, buffer, length,
+ serialno, keyref);
if (es_fwrite (buffer, length, 1, fp) != 1)
{
@@ -267,7 +334,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
gnupg_isotime_t now, protected_at, tmptime;
char *desc = NULL;
- assert (!arg->unprotected_key);
+ log_assert (!arg->unprotected_key);
arg->change_required = 0;
err = agent_unprotect (ctrl, arg->protected_key, pi->pin, protected_at,
@@ -332,6 +399,33 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
}
+/* Return true if the STRING has an %C or %c expando. */
+static int
+has_comment_expando (const char *string)
+{
+ const char *s;
+ int percent = 0;
+
+ if (!string)
+ return 0;
+
+ for (s = string; *s; s++)
+ {
+ if (percent)
+ {
+ if (*s == 'c' || *s == 'C')
+ return 1;
+ percent = 0;
+ }
+ else if (*s == '%')
+ percent = 1;
+ }
+ return 0;
+}
+
+
+
+
/* Modify a Key description, replacing certain special format
characters. List of currently supported replacements:
@@ -644,7 +738,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
}
else
{
- assert (arg.unprotected_key);
+ log_assert (arg.unprotected_key);
if (arg.change_required)
{
/* The callback told as that the user should change their
@@ -652,7 +746,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
size_t canlen, erroff;
gcry_sexp_t s_skey;
- assert (arg.unprotected_key);
+ log_assert (arg.unprotected_key);
canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
rc = gcry_sexp_sscan (&s_skey, &erroff,
(char*)arg.unprotected_key, canlen);
@@ -695,10 +789,13 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
/* Read the key identified by GRIP from the private key directory and
- return it as an gcrypt S-expression object in RESULT. On failure
- returns an error code and stores NULL at RESULT. */
+ * return it as an gcrypt S-expression object in RESULT. If R_KEYMETA
+ * is not NULl and the extended key format is used, the meta data
+ * items are stored there. However the "Key:" item is removed from
+ * it. On failure returns an error code and stores NULL at RESULT and
+ * R_KEYMETA. */
static gpg_error_t
-read_key_file (const unsigned char *grip, gcry_sexp_t *result)
+read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
{
gpg_error_t err;
char *fname;
@@ -711,6 +808,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
char first;
*result = NULL;
+ if (r_keymeta)
+ *r_keymeta = NULL;
bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
@@ -749,7 +848,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
if (first != '(')
{
/* Key is in extended format. */
- nvc_t pk;
+ nvc_t pk = NULL;
int line;
err = nvc_parse_private_key (&pk, &line, fp);
@@ -761,12 +860,17 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
else
{
err = nvc_get_private_key (pk, result);
- nvc_release (pk);
if (err)
log_error ("error getting private key from '%s': %s\n",
fname, gpg_strerror (err));
+ else
+ nvc_delete_named (pk, "Key:");
}
+ if (!err && r_keymeta)
+ *r_keymeta = pk;
+ else
+ nvc_release (pk);
xfree (fname);
return err;
}
@@ -866,6 +970,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
unsigned char *buf;
size_t len, buflen, erroff;
gcry_sexp_t s_skey;
+ nvc_t keymeta = NULL;
+ char *desc_text_buffer = NULL; /* Used in case we extend DESC_TEXT. */
*result = NULL;
if (shadow_info)
@@ -873,7 +979,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
if (r_passphrase)
*r_passphrase = NULL;
- err = read_key_file (grip, &s_skey);
+ err = read_key_file (grip, &s_skey, &keymeta);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_ENOENT)
@@ -886,7 +992,11 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
now. */
err = make_canon_sexp (s_skey, &buf, &len);
if (err)
- return err;
+ {
+ nvc_release (keymeta);
+ xfree (desc_text_buffer);
+ return err;
+ }
switch (agent_private_key_type (buf))
{
@@ -911,25 +1021,43 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
case PRIVATE_KEY_PROTECTED:
{
char *desc_text_final;
- char *comment = NULL;
+ char *comment_buffer = NULL;
+ const char *comment = NULL;
/* 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;
+ * display purposes; i.e. all stuff beyond a Nul character is
+ * ignored. If a "Label" entry is available in the meta data
+ * this is used instead of the s-ecpression comment. */
+ if (keymeta && (comment = nvc_get_string (keymeta, "Label:")))
+ {
+ if (strchr (comment, '\n')
+ && (comment_buffer = linefeed_to_percent0A (comment)))
+ comment = comment_buffer;
+ /* In case DESC_TEXT has no escape pattern for a comment
+ * we append one. */
+ if (desc_text && !has_comment_expando (desc_text))
+ {
+ desc_text_buffer = strconcat (desc_text, "%0A%C", NULL);
+ if (desc_text_buffer)
+ desc_text = desc_text_buffer;
+ }
+ }
+ else
+ {
+ 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);
- }
+ comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
+ if (comment_sexp)
+ comment_buffer = gcry_sexp_nth_string (comment_sexp, 1);
+ gcry_sexp_release (comment_sexp);
+ comment = comment_buffer;
+ }
desc_text_final = NULL;
if (desc_text)
err = agent_modify_description (desc_text, comment, s_skey,
&desc_text_final);
- gcry_free (comment);
+ gcry_free (comment_buffer);
if (!err)
{
@@ -984,6 +1112,8 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
xfree (*r_passphrase);
*r_passphrase = NULL;
}
+ nvc_release (keymeta);
+ xfree (desc_text_buffer);
return err;
}
@@ -1000,10 +1130,14 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
xfree (*r_passphrase);
*r_passphrase = NULL;
}
+ nvc_release (keymeta);
+ xfree (desc_text_buffer);
return err;
}
*result = s_skey;
+ nvc_release (keymeta);
+ xfree (desc_text_buffer);
return 0;
}
@@ -1203,7 +1337,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
*result = NULL;
- err = read_key_file (grip, &s_skey);
+ err = read_key_file (grip, &s_skey, NULL);
if (!err)
*result = s_skey;
return err;
@@ -1230,6 +1364,7 @@ agent_public_key_from_file (ctrl_t ctrl,
gcry_sexp_t uri_sexp, comment_sexp;
const char *uri, *comment;
size_t uri_length, comment_length;
+ int uri_intlen, comment_intlen;
char *format, *p;
void *args[2+7+2+2+1]; /* Size is 2 + max. # of elements + 2 for uri + 2
for comment + end-of-list. */
@@ -1241,7 +1376,7 @@ agent_public_key_from_file (ctrl_t ctrl,
*result = NULL;
- err = read_key_file (grip, &s_skey);
+ err = read_key_file (grip, &s_skey, NULL);
if (err)
return err;
@@ -1278,7 +1413,7 @@ agent_public_key_from_file (ctrl_t ctrl,
such a task. After all that is what we do in protect.c. Need
to find common patterns and write a straightformward API to use
them. */
- assert (sizeof (size_t) <= sizeof (void*));
+ log_assert (sizeof (size_t) <= sizeof (void*));
format = xtrymalloc (15+4+7*npkey+10+15+1+1);
if (!format)
@@ -1303,27 +1438,29 @@ agent_public_key_from_file (ctrl_t ctrl,
*p++ = '(';
*p++ = *s++;
p = stpcpy (p, " %m)");
- assert (argidx < DIM (args));
+ log_assert (argidx < DIM (args));
args[argidx++] = &array[idx];
}
*p++ = ')';
if (uri)
{
p = stpcpy (p, "(uri %b)");
- assert (argidx+1 < DIM (args));
- args[argidx++] = (void *)&uri_length;
+ log_assert (argidx+1 < DIM (args));
+ uri_intlen = (int)uri_length;
+ args[argidx++] = (void *)&uri_intlen;
args[argidx++] = (void *)&uri;
}
if (comment)
{
p = stpcpy (p, "(comment %b)");
- assert (argidx+1 < DIM (args));
- args[argidx++] = (void *)&comment_length;
+ log_assert (argidx+1 < DIM (args));
+ comment_intlen = (int)comment_length;
+ args[argidx++] = (void *)&comment_intlen;
args[argidx++] = (void*)&comment;
}
*p++ = ')';
*p = 0;
- assert (argidx < DIM (args));
+ log_assert (argidx < DIM (args));
args[argidx] = NULL;
err = gcry_sexp_build_array (&list, NULL, format, args);
@@ -1386,7 +1523,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
{
gcry_sexp_t sexp;
- err = read_key_file (grip, &sexp);
+ err = read_key_file (grip, &sexp, NULL);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_ENOENT)
@@ -1420,7 +1557,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
if (!err)
{
n = gcry_sexp_canon_len (s, 0, NULL, NULL);
- assert (n);
+ log_assert (n);
*r_shadow_info = xtrymalloc (n);
if (!*r_shadow_info)
err = gpg_error_from_syserror ();
@@ -1470,7 +1607,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
char *default_desc = NULL;
int key_type;
- err = read_key_file (grip, &s_skey);
+ err = read_key_file (grip, &s_skey, NULL);
if (gpg_err_code (err) == GPG_ERR_ENOENT)
err = gpg_error (GPG_ERR_NO_SECKEY);
if (err)
@@ -1580,6 +1717,13 @@ agent_write_shadow_key (const unsigned char *grip,
unsigned char *shdkey;
size_t len;
+ /* Just in case some caller did not parse the stuff correctly, skip
+ * leading spaces. */
+ while (spacep (serialno))
+ serialno++;
+ while (spacep (keyid))
+ keyid++;
+
shadow_info = make_shadow_info (serialno, keyid);
if (!shadow_info)
return gpg_error_from_syserror ();
@@ -1593,7 +1737,7 @@ agent_write_shadow_key (const unsigned char *grip,
}
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
- err = agent_write_private_key (grip, shdkey, len, force);
+ err = agent_write_private_key (grip, shdkey, len, force, serialno, keyid);
xfree (shdkey);
if (err)
log_error ("error writing key: %s\n", gpg_strerror (err));