aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to 'agent')
-rw-r--r--agent/agent.h18
-rw-r--r--agent/call-pinentry.c4
-rw-r--r--agent/call-scd.c62
-rw-r--r--agent/command.c145
-rw-r--r--agent/divert-scd.c11
-rw-r--r--agent/protect.c6
6 files changed, 215 insertions, 31 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 45bc507e3..030b29520 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -34,6 +34,7 @@
#include "../common/membuf.h"
#include "../common/sysutils.h" /* (gnupg_fd_t) */
#include "../common/session-env.h"
+#include "../common/shareddefs.h"
/* To convey some special hash algorithms we use algorithm numbers
reserved for application use. */
@@ -46,16 +47,6 @@
#define MAX_DIGEST_LEN 64
-/* Values for the pinentry mode. */
-typedef enum
- {
- PINENTRY_MODE_ASK = 0, /* Ask via pinentry (default). */
- PINENTRY_MODE_CANCEL, /* Always return a cancel error. */
- PINENTRY_MODE_ERROR, /* Return error code for no pinentry. */
- PINENTRY_MODE_LOOPBACK,/* Use an inquiry to get the value. */
- }
-pinentry_mode_t;
-
/* A large struct name "opt" to keep global flags */
struct
@@ -421,6 +412,8 @@ int divert_pkdecrypt (ctrl_t ctrl,
char **r_buf, size_t *r_len);
int divert_generic_cmd (ctrl_t ctrl,
const char *cmdline, void *assuan_context);
+int divert_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *id, const char *keydata, size_t keydatalen);
/*-- call-scd.c --*/
@@ -454,6 +447,11 @@ int agent_card_pkdecrypt (ctrl_t ctrl,
int agent_card_readcert (ctrl_t ctrl,
const char *id, char **r_buf, size_t *r_buflen);
int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf);
+int agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *id, const char *keydata,
+ size_t keydatalen,
+ int (*getpin_cb)(void *, const char *, char*, size_t),
+ void *getpin_cb_arg);
gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result);
int agent_card_scd (ctrl_t ctrl, const char *cmdline,
int (*getpin_cb)(void *, const char *, char*, size_t),
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
index c37831a05..c6b6b5282 100644
--- a/agent/call-pinentry.c
+++ b/agent/call-pinentry.c
@@ -1266,9 +1266,9 @@ agent_popup_message_stop (ctrl_t ctrl)
/* Now wait for the thread to terminate. */
rc = npth_join (popup_tid, NULL);
- if (!rc)
+ if (rc)
log_debug ("agent_popup_message_stop: pth_join failed: %s\n",
- strerror (errno));
+ strerror (rc));
/* Thread IDs are opaque, but we try our best here by resetting it
to the same content that a static global variable has. */
memset (&popup_tid, '\0', sizeof (popup_tid));
diff --git a/agent/call-scd.c b/agent/call-scd.c
index 2bda3779d..cbe4d1c34 100644
--- a/agent/call-scd.c
+++ b/agent/call-scd.c
@@ -722,7 +722,7 @@ inq_needpin (void *opaque, const char *line)
rc = assuan_send_data (parm->ctx, pin, pinlen);
xfree (pin);
}
- else if (!strncmp (line, "POPUPKEYPADPROMPT", 17)
+ else if (!strncmp (line, "POPUPPINPADPROMPT", 17)
&& (line[17] == ' ' || !line[17]))
{
line += 17;
@@ -731,7 +731,7 @@ inq_needpin (void *opaque, const char *line)
rc = parm->getpin_cb (parm->getpin_cb_arg, line, NULL, 1);
}
- else if (!strncmp (line, "DISMISSKEYPADPROMPT", 19)
+ else if (!strncmp (line, "DISMISSPINPADPROMPT", 19)
&& (line[19] == ' ' || !line[19]))
{
rc = parm->getpin_cb (parm->getpin_cb_arg, "", NULL, 0);
@@ -1050,6 +1050,64 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf)
}
+struct writekey_parm_s
+{
+ assuan_context_t ctx;
+ int (*getpin_cb)(void *, const char *, char*, size_t);
+ void *getpin_cb_arg;
+ assuan_context_t passthru;
+ int any_inq_seen;
+ /**/
+ const unsigned char *keydata;
+ size_t keydatalen;
+};
+
+/* Handle a KEYDATA inquiry. Note, we only send the data,
+ assuan_transact takes care of flushing and writing the end */
+static gpg_error_t
+inq_writekey_parms (void *opaque, const char *line)
+{
+ struct writekey_parm_s *parm = opaque;
+
+ if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
+ return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+ else
+ return inq_needpin (opaque, line);
+}
+
+
+int
+agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *id, const char *keydata, size_t keydatalen,
+ int (*getpin_cb)(void *, const char *, char*, size_t),
+ void *getpin_cb_arg)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ struct writekey_parm_s parms;
+
+ (void)serialno;
+ rc = start_scd (ctrl);
+ if (rc)
+ return rc;
+
+ snprintf (line, DIM(line)-1, "WRITEKEY %s%s", force ? "--force " : "", id);
+ line[DIM(line)-1] = 0;
+ parms.ctx = ctrl->scd_local->ctx;
+ parms.getpin_cb = getpin_cb;
+ parms.getpin_cb_arg = getpin_cb_arg;
+ parms.passthru = 0;
+ parms.any_inq_seen = 0;
+ parms.keydata = keydata;
+ parms.keydatalen = keydatalen;
+
+ rc = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL,
+ inq_writekey_parms, &parms, NULL, NULL);
+ if (parms.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED ||
+ gpg_err_code(rc) == GPG_ERR_ASS_CANCELED))
+ rc = cancel_inquire (ctrl, rc);
+ return unlock_scd (ctrl, rc);
+}
/* Type used with the card_getattr_cb. */
struct card_getattr_parm_s {
diff --git a/agent/command.c b/agent/command.c
index 3ba921be4..2844398f6 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -2119,9 +2119,133 @@ cmd_export_key (assuan_context_t ctx, char *line)
return leave_cmd (ctx, err);
}
+
+static const char hlp_keytocard[] =
+ "KEYTOCARD [--force] <hexstring_with_keygrip> <serialno> <id> <timestamp>\n"
+ "\n";
+static gpg_error_t
+cmd_keytocard (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int force;
+ gpg_error_t err = 0;
+ unsigned char grip[20];
+ gcry_sexp_t s_skey = NULL;
+ gcry_sexp_t s_pkey = NULL;
+ unsigned char *keydata;
+ size_t keydatalen, timestamplen;
+ const char *serialno, *timestamp_str, *id;
+ unsigned char *shadow_info;
+ unsigned char *shdkey;
+ time_t timestamp;
+
+ force = has_option (line, "--force");
+ line = skip_options (line);
+ err = parse_keygrip (ctx, line, grip);
+ if (err)
+ return err;
+ if (agent_key_available (grip))
+ return gpg_error (GPG_ERR_NO_SECKEY);
+
+ line += 40;
+ while (*line && (*line == ' ' || *line == '\t'))
+ line++;
+ serialno = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (!*line)
+ return gpg_error (GPG_ERR_MISSING_VALUE);
+ *line = '\0';
+ line++;
+ while (*line && (*line == ' ' || *line == '\t'))
+ line++;
+ id = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (!*line)
+ return gpg_error (GPG_ERR_MISSING_VALUE);
+ *line = '\0';
+ line++;
+ while (*line && (*line == ' ' || *line == '\t'))
+ line++;
+ timestamp_str = line;
+ while (*line && (*line != ' ' && *line != '\t'))
+ line++;
+ if (*line)
+ *line = '\0';
+ timestamplen = line - timestamp_str;
+ if (timestamplen != 15)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip,
+ NULL, CACHE_MODE_IGNORE, NULL, &s_skey, NULL);
+ if (err)
+ return err;
+ if (!s_skey)
+ /* Key is on a smartcard already. */
+ return gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+
+ keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
+ keydata = xtrymalloc_secure (keydatalen + 30);
+ if (keydata == NULL)
+ {
+ gcry_sexp_release (s_skey);
+ return gpg_error_from_syserror ();
+ }
+
+ gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, keydata, keydatalen);
+ gcry_sexp_release (s_skey);
+ /* Add timestamp "created-at" in the private key */
+ timestamp = isotime2epoch (timestamp_str);
+ snprintf (keydata+keydatalen-1, 30, "(10:created-at10:%010lu))", timestamp);
+ keydatalen += 10 + 19 - 1;
+ err = divert_writekey (ctrl, force, serialno, id, keydata, keydatalen);
+ if (err)
+ {
+ xfree (keydata);
+ goto leave;
+ }
+ xfree (keydata);
+
+ err = agent_public_key_from_file (ctrl, grip, &s_pkey);
+ if (err)
+ goto leave;
+
+ shadow_info = make_shadow_info (serialno, id);
+ if (!shadow_info)
+ {
+ err = gpg_error (GPG_ERR_ENOMEM);
+ gcry_sexp_release (s_pkey);
+ goto leave;
+ }
+ keydatalen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+ keydata = xtrymalloc (keydatalen);
+ if (keydata == NULL)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_sexp_release (s_pkey);
+ goto leave;
+ }
+ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keydata, keydatalen);
+ gcry_sexp_release (s_pkey);
+ err = agent_shadow_key (keydata, shadow_info, &shdkey);
+ xfree (keydata);
+ xfree (shadow_info);
+ if (err)
+ {
+ log_error ("shadowing the key failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ keydatalen = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
+ err = agent_write_private_key (grip, shdkey, keydatalen, 1);
+ xfree (shdkey);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
static const char hlp_getval[] =
"GETVAL <key>\n"
@@ -2548,21 +2672,13 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
ctrl->server_local->allow_pinentry_notify = 1;
else if (!strcmp (key, "pinentry-mode"))
{
- if (!strcmp (value, "ask") || !strcmp (value, "default"))
- ctrl->pinentry_mode = PINENTRY_MODE_ASK;
- else if (!strcmp (value, "cancel"))
- ctrl->pinentry_mode = PINENTRY_MODE_CANCEL;
- else if (!strcmp (value, "error"))
- ctrl->pinentry_mode = PINENTRY_MODE_ERROR;
- else if (!strcmp (value, "loopback"))
- {
- if (opt.allow_loopback_pinentry)
- ctrl->pinentry_mode = PINENTRY_MODE_LOOPBACK;
- else
- err = gpg_error (GPG_ERR_NOT_SUPPORTED);
- }
- else
+ int tmp = parse_pinentry_mode (value);
+ if (tmp == -1)
err = gpg_error (GPG_ERR_INV_VALUE);
+ else if (tmp == PINENTRY_MODE_LOOPBACK && !opt.allow_loopback_pinentry)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ ctrl->pinentry_mode = tmp;
}
else if (!strcmp (key, "cache-ttl-opt-preset"))
{
@@ -2682,6 +2798,7 @@ register_commands (assuan_context_t ctx)
{ "KILLAGENT", cmd_killagent, hlp_killagent },
{ "RELOADAGENT", cmd_reloadagent,hlp_reloadagent },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
+ { "KEYTOCARD", cmd_keytocard, hlp_keytocard },
{ NULL }
};
int i, rc;
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index 656d5cdf1..5fb037ee5 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -223,7 +223,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
else if (info && *info == '|')
log_debug ("pin_cb called without proper PIN info hack\n");
- /* If BUF has been passed as NULL, we are in keypad mode: The
+ /* If BUF has been passed as NULL, we are in pinpad mode: The
callback opens the popup and immediatley returns. */
if (!buf)
{
@@ -239,7 +239,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
char *desc;
if ( asprintf (&desc,
- _("%s%%0A%%0AUse the reader's keypad for input."),
+ _("%s%%0A%%0AUse the reader's pinpad for input."),
info) < 0 )
rc = gpg_error_from_syserror ();
else
@@ -442,6 +442,13 @@ divert_pkdecrypt (ctrl_t ctrl,
return rc;
}
+int
+divert_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *id, const char *keydata, size_t keydatalen)
+{
+ return agent_card_writekey (ctrl, force, serialno, id, keydata, keydatalen,
+ getpin_cb, ctrl);
+}
int
divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context)
diff --git a/agent/protect.c b/agent/protect.c
index d26573d11..3e2cbb94e 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1075,7 +1075,11 @@ hash_passphrase (const char *passphrase, int hashalgo,
unsigned long s2kcount,
unsigned char *key, size_t keylen)
{
-
+ /* The key derive function does not support a zero length string for
+ the passphrase in the S2K modes. Return a better suited error
+ code than GPG_ERR_INV_DATA. */
+ if (!passphrase || !*passphrase)
+ return gpg_error (GPG_ERR_NO_PASSPHRASE);
return gcry_kdf_derive (passphrase, strlen (passphrase),
s2kmode == 3? GCRY_KDF_ITERSALTED_S2K :
s2kmode == 1? GCRY_KDF_SALTED_S2K :