#include "agent.h"
#include "../common/i18n.h"
#include "../common/ssh-utils.h"
-#include "../common/name-value.h"
#ifndef O_BINARY
#define O_BINARY 0
};
-/* Repalce all linefeeds in STRING by "%0A" and return a new malloced
+/* Replace 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)
goto leave;
err = nvc_write (pk, fp);
+ if (!err)
+ err = es_fflush (fp);
if (err)
{
log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
}
+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
{
/* We need to give the other thread a chance to actually put
it into the cache. */
- npth_sleep (1);
+ gnupg_sleep (1);
goto retry;
}
/* Timeout - better call pinentry now the plain way. */
}
+/*
+ * Prompt a user the card insertion, when it's not available yet.
+ */
+static gpg_error_t
+prompt_for_card (ctrl_t ctrl, const unsigned char *grip,
+ nvc_t keymeta, const unsigned char *shadow_info)
+{
+ char *serialno;
+ char *desc;
+ char *want_sn = NULL;
+ int len;
+ gpg_error_t err;
+ char hexgrip[41];
+ char *comment_buffer = NULL;
+ const char *comment = NULL;
+ int refuse_prompt = 0;
+
+ bin2hex (grip, 20, hexgrip);
+
+ if (keymeta)
+ {
+ const char *p;
+
+ if ((p = nvc_get_string (keymeta, "Prompt:")) && !strcmp (p, "no"))
+ refuse_prompt = 1;
+
+ if ((p = nvc_get_string (keymeta, "Label:")))
+ {
+ if (strchr (p, '\n')
+ && (comment_buffer = linefeed_to_percent0A (p)))
+ comment = comment_buffer;
+ else
+ comment = p;
+ }
+ }
+
+ err = parse_shadow_info (shadow_info, &want_sn, NULL, NULL);
+ if (err)
+ return err;
+
+ len = want_sn? strlen (want_sn) : 0;
+ if (len == 32 && !strncmp (want_sn, "D27600012401", 12))
+ {
+ /* This is an OpenPGP card - reformat */
+ if (!strncmp (want_sn+16, "0006", 4))
+ {
+ /* This is a Yubikey. Print the s/n as it would be printed
+ * on Yubikey 5. Example: D2760001240100000006120808620000
+ * mmmm^^^^^^^^ */
+ unsigned long sn;
+
+ sn = atoi_4 (want_sn+20) * 10000;
+ sn += atoi_4 (want_sn+24);
+ snprintf (want_sn, 32, "%lu %03lu %03lu",
+ (sn/1000000ul), (sn/1000ul % 1000ul), (sn % 1000ul));
+ }
+ else /* Default is the Zeitcontrol card print format. */
+ {
+ memmove (want_sn, want_sn+16, 4);
+ want_sn[4] = ' ';
+ memmove (want_sn+5, want_sn+20, 8);
+ want_sn[13] = 0;
+ }
+ }
+ else if (len == 20 && want_sn[19] == '0')
+ {
+ /* We assume that a 20 byte serial number is a standard one
+ * which has the property to have a zero in the last nibble (Due
+ * to BCD representation). We don't display this '0' because it
+ * may confuse the user. */
+ want_sn[19] = 0;
+ }
+
+ for (;;)
+ {
+ /* Scan device(s), and check if key for GRIP is available. */
+ err = agent_card_serialno (ctrl, &serialno, NULL);
+ if (!err)
+ {
+ struct card_key_info_s *keyinfo;
+
+ xfree (serialno);
+ err = agent_card_keyinfo (ctrl, hexgrip, 0, &keyinfo);
+ if (!err)
+ {
+ /* Key for GRIP found, use it. */
+ agent_card_free_keyinfo (keyinfo);
+ break;
+ }
+ }
+
+ /* Card is not available. Prompt the insertion. */
+ if (refuse_prompt)
+ {
+ err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+ break;
+ }
+
+ if (asprintf (&desc,
+ "%s:%%0A%%0A"
+ " %s%%0A"
+ " %s",
+ L_("Please insert the card with serial number"),
+ want_sn ? want_sn : "",
+ comment? comment:"") < 0)
+ err = out_of_core ();
+ else
+ {
+ err = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
+ if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK &&
+ gpg_err_code (err) == GPG_ERR_NO_PIN_ENTRY)
+ err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+
+ xfree (desc);
+ }
+
+ if (err)
+ break;
+ }
+
+ xfree (want_sn);
+ gcry_free (comment_buffer);
+ return err;
+}
+
+
/* Return the secret key as an S-Exp in RESULT after locating it using
- the GRIP. If the operation shall be diverted to a token, an
+ the GRIP. Caller should set GRIP=NULL, when a key in a file is
+ intended to be used for cryptographic operation. In this case,
+ CTRL->keygrip is used to locate the file, and it may ask a user for
+ confirmation. If the operation shall be diverted to a token, an
allocated S-expression with the shadow_info part from the file is
stored at SHADOW_INFO; if not NULL will be stored at SHADOW_INFO.
CACHE_MODE defines now the cache shall be used. DESC_TEXT may be
const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
- gcry_sexp_t *result, char **r_passphrase)
+ gcry_sexp_t *result, char **r_passphrase,
+ time_t *r_timestamp)
{
gpg_error_t err;
unsigned char *buf;
*shadow_info = NULL;
if (r_passphrase)
*r_passphrase = NULL;
+ if (r_timestamp)
+ *r_timestamp = (time_t)(-1);
- err = read_key_file (grip, &s_skey, &keymeta);
- if (err)
- {
- if (gpg_err_code (err) == GPG_ERR_ENOENT)
- err = gpg_error (GPG_ERR_NO_SECKEY);
- return err;
- }
+ if (!grip && !ctrl->have_keygrip)
+ return gpg_error (GPG_ERR_NO_SECKEY);
+
+ err = read_key_file (grip? grip : ctrl->keygrip, &s_skey, &keymeta);
/* For use with the protection functions we also need the key as an
canonical encoded S-expression in a buffer. Create this buffer
return err;
}
+ if (r_timestamp && keymeta)
+ {
+ const char *created = nvc_get_string (keymeta, "Created:");
+
+ if (created)
+ *r_timestamp = isotime2epoch (created);
+ }
+
+ if (!grip && keymeta)
+ {
+ const char *ask_confirmation = nvc_get_string (keymeta, "Confirm:");
+
+ if (ask_confirmation
+ && ((!strcmp (ask_confirmation, "restricted") && ctrl->restricted)
+ || !strcmp (ask_confirmation, "yes")))
+ {
+ char hexgrip[40+4+1];
+ char *prompt;
+ char *comment_buffer = NULL;
+ const char *comment = NULL;
+
+ bin2hex (ctrl->keygrip, 20, hexgrip);
+
+ if ((comment = nvc_get_string (keymeta, "Label:")))
+ {
+ if (strchr (comment, '\n')
+ && (comment_buffer = linefeed_to_percent0A (comment)))
+ comment = comment_buffer;
+ }
+
+ prompt = xtryasprintf (L_("Requested the use of key%%0A"
+ " %s%%0A"
+ " %s%%0A"
+ "Do you want to allow this?"),
+ hexgrip, comment? comment:"");
+
+ gcry_free (comment_buffer);
+
+ err = agent_get_confirmation (ctrl, prompt,
+ L_("Allow"), L_("Deny"), 0);
+ xfree (prompt);
+
+ if (err)
+ return err;
+ }
+ }
+
switch (agent_private_key_type (buf))
{
case PRIVATE_KEY_CLEAR:
&desc_text_final);
gcry_free (comment_buffer);
- if (!err)
- {
- err = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip,
- cache_mode, lookup_ttl, r_passphrase);
- if (err)
- log_error ("failed to unprotect the secret key: %s\n",
- gpg_strerror (err));
- }
+ if (!err)
+ {
+ err = unprotect (ctrl, cache_nonce, desc_text_final, &buf,
+ grip? grip : ctrl->keygrip,
+ cache_mode, lookup_ttl, r_passphrase);
+ if (err)
+ log_error ("failed to unprotect the secret key: %s\n",
+ gpg_strerror (err));
+ }
xfree (desc_text_final);
}
if (shadow_info)
{
const unsigned char *s;
+ unsigned char *shadow_type;
size_t n;
- err = agent_get_shadow_info (buf, &s);
+ err = agent_get_shadow_info_type (buf, &s, &shadow_type);
if (!err)
{
n = gcry_sexp_canon_len (s, 0, NULL,NULL);
log_assert (n);
*shadow_info = xtrymalloc (n);
if (!*shadow_info)
- err = out_of_core ();
+ {
+ err = out_of_core ();
+ goto shadow_error;
+ }
else
{
memcpy (*shadow_info, s, n);
- err = 0;
+ /*
+ * When it's a key on card (not on tpm2), maks sure
+ * it's available.
+ */
+ if (strcmp (shadow_type, "t1-v1") == 0 && !grip)
+ err = prompt_for_card (ctrl, ctrl->keygrip,
+ keymeta, *shadow_info);
}
}
- if (err)
+ else
+ shadow_error:
log_error ("get_shadow_info failed: %s\n", gpg_strerror (err));
+
+ xfree (shadow_type);
}
else
err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
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;
*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;
at RESULT. This function extracts the public key from the private
key database. On failure an error code is returned and NULL stored
at RESULT. */
-gpg_error_t
-agent_public_key_from_file (ctrl_t ctrl,
- const unsigned char *grip,
- gcry_sexp_t *result)
+static gpg_error_t
+public_key_from_file (ctrl_t ctrl, const unsigned char *grip,
+ gcry_sexp_t *result, int for_ssh)
{
gpg_error_t err;
int i, idx;
gcry_sexp_t s_skey;
+ nvc_t keymeta = NULL;
const char *algoname, *elems;
int npkey;
gcry_mpi_t array[10];
*result = NULL;
- err = read_key_file (grip, &s_skey, NULL);
+ err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL);
if (err)
return err;
+ if (for_ssh)
+ {
+ /* Use-for-ssh: yes */
+ int is_ssh = 0;
+
+ if (keymeta == NULL)
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+
+ is_ssh = nvc_get_boolean (keymeta, "Use-for-ssh:");
+ nvc_release (keymeta);
+ keymeta = NULL;
+
+ if (!is_ssh)
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
for (i=0; i < DIM (array); i++)
array[i] = NULL;
return err;
}
+gpg_error_t
+agent_public_key_from_file (ctrl_t ctrl,
+ const unsigned char *grip,
+ gcry_sexp_t *result)
+{
+ return public_key_from_file (ctrl, grip, result, 0);
+}
+
+gpg_error_t
+agent_ssh_key_from_file (ctrl_t ctrl,
+ const unsigned char *grip,
+ gcry_sexp_t *result)
+{
+ return public_key_from_file (ctrl, grip, result, 1);
+}
+
/* Check whether the secret key identified by GRIP is available.
Returns 0 is the key is available. */