diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/agent.h | 18 | ||||
-rw-r--r-- | agent/call-pinentry.c | 4 | ||||
-rw-r--r-- | agent/call-scd.c | 62 | ||||
-rw-r--r-- | agent/command.c | 145 | ||||
-rw-r--r-- | agent/divert-scd.c | 11 | ||||
-rw-r--r-- | agent/protect.c | 6 |
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 : |