aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to 'agent')
-rw-r--r--agent/agent.h36
-rw-r--r--agent/call-pinentry.c217
-rw-r--r--agent/call-scd.c398
-rw-r--r--agent/command-ssh.c165
-rw-r--r--agent/command.c115
-rw-r--r--agent/divert-scd.c74
-rw-r--r--agent/findkey.c12
-rw-r--r--agent/gpg-agent.c37
-rw-r--r--agent/keyformat.txt6
-rw-r--r--agent/learncard.c2
-rw-r--r--agent/pksign.c82
-rw-r--r--agent/protect.c41
12 files changed, 673 insertions, 512 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 9baf59601..0f804cd8b 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -124,7 +124,11 @@ struct
passphrase change. */
int enable_passphrase_history;
- /* If set the extended key format is used for new keys. */
+ /* If set the extended key format is used for new keys. Note that
+ * this may vave the value 2 in which case
+ * --disable-extended-key-format won't have any effect and thus
+ * effectivley locking it. This is required to support existing
+ * profiles which lock the use of --enable-extended-key-format. */
int enable_extended_key_format;
int running_detached; /* We are running detached from the tty. */
@@ -266,6 +270,14 @@ struct server_control_s
};
+/* Status of pinentry. */
+enum
+ {
+ PINENTRY_STATUS_CLOSE_BUTTON = 1 << 0,
+ PINENTRY_STATUS_PIN_REPEATED = 1 << 8,
+ PINENTRY_STATUS_PASSWORD_FROM_CACHE = 1 << 9
+ };
+
/* Information pertaining to pinentry requests. */
struct pin_entry_info_s
{
@@ -275,7 +287,8 @@ struct pin_entry_info_s
int failed_tries; /* Number of tries so far failed. */
int with_qualitybar; /* Set if the quality bar should be displayed. */
int with_repeat; /* Request repetition of the passphrase. */
- int repeat_okay; /* Repetition worked. */
+ int repeat_okay; /* Repetition worked. */
+ unsigned int status; /* Status. */
gpg_error_t (*check_cb)(struct pin_entry_info_s *); /* CB used to check
the PIN */
void *check_cb_arg; /* optional argument which might be of use in the CB */
@@ -488,6 +501,7 @@ gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
char **passphrase_addr);
/*-- protect.c --*/
+void set_s2k_calibration_time (unsigned int milliseconds);
unsigned long get_calibrated_s2k_count (void);
unsigned long get_standard_s2k_count (void);
unsigned char get_standard_s2k_count_rfc4880 (void);
@@ -538,15 +552,15 @@ int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
char **r_buf, size_t *r_len, int *r_padding);
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);
+gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *keyref,
+ const char *keydata, size_t keydatalen);
/*-- call-scd.c --*/
void initialize_module_call_scd (void);
void agent_scd_dump_state (void);
int agent_scd_check_running (void);
-void agent_scd_check_aliveness (void);
int agent_reset_scd (ctrl_t ctrl);
int agent_card_learn (ctrl_t ctrl,
void (*kpinfo_cb)(void*, const char *),
@@ -577,12 +591,12 @@ 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 *,
- const char *, char*, size_t),
- void *getpin_cb_arg);
+gpg_error_t agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
+ const char *keyref,
+ const char *keydata, size_t keydatalen,
+ int (*getpin_cb)(void *, const char *,
+ const char *, char*, size_t),
+ void *getpin_cb_arg);
gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result);
gpg_error_t agent_card_cardlist (ctrl_t ctrl, strlist_t *result);
int agent_card_scd (ctrl_t ctrl, const char *cmdline,
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
index af4eb06f2..34dde3744 100644
--- a/agent/call-pinentry.c
+++ b/agent/call-pinentry.c
@@ -85,6 +85,7 @@ struct entry_parm_s
int lines;
size_t size;
unsigned char *buffer;
+ int status;
};
@@ -98,11 +99,15 @@ void
initialize_module_call_pinentry (void)
{
static int initialized;
+ int err;
if (!initialized)
{
- if (npth_mutex_init (&entry_lock, NULL))
- initialized = 1;
+ err = npth_mutex_init (&entry_lock, NULL);
+ if (err)
+ log_fatal ("error initializing mutex: %s\n", strerror (err));
+
+ initialized = 1;
}
}
@@ -497,14 +502,16 @@ start_pinentry (ctrl_t ctrl)
{
/* Provide a few default strings for use by the pinentries. This
- may help a pinentry to avoid implementing localization code. */
+ * may help a pinentry to avoid implementing localization code.
+ * Note that gpg-agent has been set to utf-8 so that the strings
+ * are in the expected encoding. */
static const struct { const char *key, *value; int what; } tbl[] = {
- /* TRANSLATORS: These are labels for buttons etc used in
- Pinentries. An underscore indicates that the next letter
- should be used as an accelerator. Double the underscore for
- a literal one. The actual to be translated text starts after
- the second vertical bar. Note that gpg-agent has been set to
- utf-8 so that the strings are in the expected encoding. */
+ /* TRANSLATORS: These are labels for buttons etc as used in
+ * Pinentries. In your translation copy the text before the
+ * second vertical bar verbatim; translate only the following
+ * text. An underscore indicates that the next letter should be
+ * used as an accelerator. Double the underscore to have
+ * pinentry display a literal underscore. */
{ "ok", N_("|pinentry-label|_OK") },
{ "cancel", N_("|pinentry-label|_Cancel") },
{ "yes", N_("|pinentry-label|_Yes") },
@@ -888,13 +895,6 @@ setup_qualitybar (ctrl_t ctrl)
return 0;
}
-enum
- {
- PINENTRY_STATUS_CLOSE_BUTTON = 1 << 0,
- PINENTRY_STATUS_PIN_REPEATED = 1 << 8,
- PINENTRY_STATUS_PASSWORD_FROM_CACHE = 1 << 9
- };
-
/* Check the button_info line for a close action. Also check for the
PIN_REPEATED flag. */
static gpg_error_t
@@ -942,6 +942,112 @@ build_cmd_setdesc (char *line, size_t linelen, const char *desc)
+/* Watch the socket's EOF condition, while checking finish of
+ foreground thread. When EOF condition is detected, terminate
+ the pinentry process behind the assuan pipe.
+ */
+static void *
+watch_sock (void *arg)
+{
+ gnupg_fd_t *p = (gnupg_fd_t *)arg;
+ pid_t pid = assuan_get_pid (entry_ctx);
+
+ while (1)
+ {
+ int err;
+ gnupg_fd_t sock = *p;
+ fd_set fdset;
+ struct timeval timeout = { 0, 500000 };
+
+ if (sock == GNUPG_INVALID_FD)
+ return NULL;
+
+ FD_ZERO (&fdset);
+ FD_SET (FD2INT (sock), &fdset);
+ err = npth_select (FD2INT (sock)+1, &fdset, NULL, NULL, &timeout);
+
+ if (err < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return NULL;
+ }
+
+ /* Possibly, it's EOF. */
+ if (err > 0)
+ break;
+ }
+
+ if (pid == (pid_t)(-1))
+ ; /* No pid available can't send a kill. */
+#ifdef HAVE_W32_SYSTEM
+ /* Older versions of assuan set PID to 0 on Windows to indicate an
+ invalid value. */
+ else if (pid != (pid_t) INVALID_HANDLE_VALUE && pid != 0)
+ TerminateProcess ((HANDLE)pid, 1);
+#else
+ else if (pid > 0)
+ kill (pid, SIGINT);
+#endif
+
+ return NULL;
+}
+
+
+/* Ask pinentry to get a pin by "GETPIN" command, spawning a thread
+ detecting the socket's EOF.
+ */
+static gpg_error_t
+do_getpin (ctrl_t ctrl, struct entry_parm_s *parm)
+{
+ npth_attr_t tattr;
+ gpg_error_t rc;
+ int err;
+ npth_t thread;
+ int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
+ gnupg_fd_t sock_watched = ctrl->thread_startup.fd;
+
+ err = npth_attr_init (&tattr);
+ if (err)
+ {
+ log_error ("do_getpin: error npth_attr_init: %s\n", strerror (err));
+ return gpg_error_from_errno (err);
+ }
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
+
+ err = npth_create (&thread, &tattr, watch_sock, (void *)&sock_watched);
+ npth_attr_destroy (&tattr);
+ if (err)
+ {
+ log_error ("do_getpin: error spawning thread: %s\n", strerror (err));
+ return gpg_error_from_errno (err);
+ }
+
+ assuan_begin_confidential (entry_ctx);
+ rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm,
+ inq_quality, entry_ctx,
+ pinentry_status_cb, &parm->status);
+ assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
+ /* Most pinentries out in the wild return the old Assuan error code
+ for canceled which gets translated to an assuan Cancel error and
+ not to the code for a user cancel. Fix this here. */
+ if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
+ rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
+ /* Change error code in case the window close button was clicked
+ to cancel the operation. */
+ if ((parm->status & PINENTRY_STATUS_CLOSE_BUTTON)
+ && gpg_err_code (rc) == GPG_ERR_CANCELED)
+ rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
+
+ sock_watched = GNUPG_INVALID_FD;
+ err = npth_join (thread, NULL);
+ if (err)
+ log_error ("do_getpin: error joining thread: %s\n", strerror (err));
+
+ return rc;
+}
+
/* Call the Entry and ask for the PIN. We do check for a valid PIN
number here and repeat it as long as we have invalid formed
numbers. KEYINFO and CACHE_MODE are used to tell pinentry something
@@ -958,8 +1064,6 @@ agent_askpin (ctrl_t ctrl,
struct entry_parm_s parm;
const char *errtext = NULL;
int is_pin = 0;
- int saveflag;
- unsigned int pinentry_status;
if (opt.batch)
return 0; /* fixme: we should return BAD PIN */
@@ -1070,6 +1174,7 @@ agent_askpin (ctrl_t ctrl,
pininfo->with_repeat = 0; /* Pinentry does not support it. */
}
pininfo->repeat_okay = 0;
+ pininfo->status = 0;
for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
{
@@ -1101,27 +1206,8 @@ agent_askpin (ctrl_t ctrl,
return unlock_pinentry (ctrl, rc);
}
- saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
- assuan_begin_confidential (entry_ctx);
- pinentry_status = 0;
- rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
- inq_quality, entry_ctx,
- pinentry_status_cb, &pinentry_status);
- assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
- /* Most pinentries out in the wild return the old Assuan error code
- for canceled which gets translated to an assuan Cancel error and
- not to the code for a user cancel. Fix this here. */
- if (rc && gpg_err_source (rc)
- && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
- rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
-
-
- /* Change error code in case the window close button was clicked
- to cancel the operation. */
- if ((pinentry_status & PINENTRY_STATUS_CLOSE_BUTTON)
- && gpg_err_code (rc) == GPG_ERR_CANCELED)
- rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
-
+ rc = do_getpin (ctrl, &parm);
+ pininfo->status = parm.status;
if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
errtext = is_pin? L_("PIN too long")
: L_("Passphrase too long");
@@ -1145,12 +1231,19 @@ agent_askpin (ctrl_t ctrl,
/* More checks by utilizing the optional callback. */
pininfo->cb_errtext = NULL;
rc = pininfo->check_cb (pininfo);
- if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
- && pininfo->cb_errtext)
- errtext = pininfo->cb_errtext;
- else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
- || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
- errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase"));
+ /* When pinentry cache causes an error, return now. */
+ if (rc
+ && (pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
+ return unlock_pinentry (ctrl, rc);
+
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE)
+ {
+ if (pininfo->cb_errtext)
+ errtext = pininfo->cb_errtext;
+ else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
+ || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase"));
+ }
else if (rc)
return unlock_pinentry (ctrl, rc);
}
@@ -1158,12 +1251,12 @@ agent_askpin (ctrl_t ctrl,
if (!errtext)
{
if (pininfo->with_repeat
- && (pinentry_status & PINENTRY_STATUS_PIN_REPEATED))
+ && (pininfo->status & PINENTRY_STATUS_PIN_REPEATED))
pininfo->repeat_okay = 1;
return unlock_pinentry (ctrl, 0); /* okay, got a PIN or passphrase */
}
- if ((pinentry_status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
+ if ((pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
/* The password was read from the cache. Don't count this
against the retry count. */
pininfo->failed_tries --;
@@ -1183,12 +1276,9 @@ agent_get_passphrase (ctrl_t ctrl,
const char *errtext, int with_qualitybar,
const char *keyinfo, cache_mode_t cache_mode)
{
-
int rc;
char line[ASSUAN_LINELENGTH];
struct entry_parm_s parm;
- int saveflag;
- unsigned int pinentry_status;
*retpass = NULL;
if (opt.batch)
@@ -1272,24 +1362,7 @@ agent_get_passphrase (ctrl_t ctrl,
if (!parm.buffer)
return unlock_pinentry (ctrl, out_of_core ());
- saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
- assuan_begin_confidential (entry_ctx);
- pinentry_status = 0;
- rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
- inq_quality, entry_ctx,
- pinentry_status_cb, &pinentry_status);
- assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
- /* Most pinentries out in the wild return the old Assuan error code
- for canceled which gets translated to an assuan Cancel error and
- not to the code for a user cancel. Fix this here. */
- if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
- rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
- /* Change error code in case the window close button was clicked
- to cancel the operation. */
- if ((pinentry_status & PINENTRY_STATUS_CLOSE_BUTTON)
- && gpg_err_code (rc) == GPG_ERR_CANCELED)
- rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
-
+ rc = do_getpin (ctrl, &parm);
if (rc)
xfree (parm.buffer);
else
@@ -1537,14 +1610,6 @@ agent_popup_message_stop (ctrl_t ctrl)
TerminateProcess (process, 1);
}
#else
- else if (pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) )
- { /* The daemon already died. No need to send a kill. However
- because we already waited for the process, we need to tell
- assuan that it should not wait again (done by
- unlock_pinentry). */
- if (rc == pid)
- assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1);
- }
else if (pid > 0)
kill (pid, SIGINT);
#endif
diff --git a/agent/call-scd.c b/agent/call-scd.c
index 51d9abd70..b2266225e 100644
--- a/agent/call-scd.c
+++ b/agent/call-scd.c
@@ -54,17 +54,10 @@ struct scd_local_s
SCD_LOCAL_LIST (see below). */
struct scd_local_s *next_local;
- /* We need to get back to the ctrl object actually referencing this
- structure. This is really an awkward way of enumerating the local
- contexts. A much cleaner way would be to keep a global list of
- ctrl objects to enumerate them. */
- ctrl_t ctrl_backlink;
-
- assuan_context_t ctx; /* NULL or session context for the SCdaemon
- used with this connection. */
- int locked; /* This flag is used to assert proper use of
- start_scd and unlock_scd. */
-
+ assuan_context_t ctx; /* NULL or session context for the SCdaemon
+ used with this connection. */
+ unsigned int in_use: 1; /* CTX is in use. */
+ unsigned int invalid:1; /* CTX is invalid, should be released. */
};
@@ -138,7 +131,7 @@ initialize_module_call_scd (void)
{
err = npth_mutex_init (&start_scd_lock, NULL);
if (err)
- log_fatal ("error initializing mutex: %s\n", strerror (err));
+ log_fatal ("error initializing mutex: %s\n", strerror (err));
initialized = 1;
}
}
@@ -168,14 +161,33 @@ agent_scd_dump_state (void)
static int
unlock_scd (ctrl_t ctrl, int rc)
{
- if (ctrl->scd_local->locked != 1)
+ int err;
+
+ if (ctrl->scd_local->in_use == 0)
{
- log_error ("unlock_scd: invalid lock count (%d)\n",
- ctrl->scd_local->locked);
+ log_error ("unlock_scd: CTX is not in use\n");
if (!rc)
rc = gpg_error (GPG_ERR_INTERNAL);
}
- ctrl->scd_local->locked = 0;
+ err = npth_mutex_lock (&start_scd_lock);
+ if (err)
+ {
+ log_error ("failed to acquire the start_scd lock: %s\n", strerror (err));
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ ctrl->scd_local->in_use = 0;
+ if (ctrl->scd_local->invalid)
+ {
+ assuan_release (ctrl->scd_local->ctx);
+ ctrl->scd_local->ctx = NULL;
+ ctrl->scd_local->invalid = 0;
+ }
+ err = npth_mutex_unlock (&start_scd_lock);
+ if (err)
+ {
+ log_error ("failed to release the start_scd lock: %s\n", strerror (err));
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
return rc;
}
@@ -191,6 +203,86 @@ atfork_cb (void *opaque, int where)
}
+static void *
+wait_child_thread (void *arg)
+{
+ int err;
+ struct scd_local_s *sl;
+
+#ifdef HAVE_W32_SYSTEM
+ HANDLE pid = (HANDLE)arg;
+
+ npth_unprotect ();
+ WaitForSingleObject ((HANDLE)pid, INFINITE);
+ npth_protect ();
+ log_info ("scdaemon finished\n");
+#else
+ int wstatus;
+ pid_t pid = (pid_t)(uintptr_t)arg;
+
+ again:
+ npth_unprotect ();
+ err = waitpid (pid, &wstatus, 0);
+ npth_protect ();
+
+ if (err < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ log_error ("waitpid failed: %s\n", strerror (errno));
+ return NULL;
+ }
+ else
+ {
+ if (WIFEXITED (wstatus))
+ log_info ("scdaemon finished (status %d)\n", WEXITSTATUS (wstatus));
+ else if (WIFSIGNALED (wstatus))
+ log_info ("scdaemon killed by signal %d\n", WTERMSIG (wstatus));
+ else
+ {
+ if (WIFSTOPPED (wstatus))
+ log_info ("scdaemon stopped by signal %d\n", WSTOPSIG (wstatus));
+ goto again;
+ }
+ }
+#endif
+
+ err = npth_mutex_lock (&start_scd_lock);
+ if (err)
+ {
+ log_error ("failed to acquire the start_scd lock: %s\n",
+ strerror (err));
+ }
+ else
+ {
+ assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1);
+
+ for (sl = scd_local_list; sl; sl = sl->next_local)
+ {
+ sl->invalid = 1;
+ if (!sl->in_use && sl->ctx)
+ {
+ assuan_release (sl->ctx);
+ sl->ctx = NULL;
+ }
+ }
+
+ primary_scd_ctx = NULL;
+ primary_scd_ctx_reusable = 0;
+
+ xfree (socket_name);
+ socket_name = NULL;
+
+ err = npth_mutex_unlock (&start_scd_lock);
+ if (err)
+ log_error ("failed to release the start_scd lock after waitpid: %s\n",
+ strerror (err));
+ }
+
+ return NULL;
+}
+
+
/* Fork off the SCdaemon if this has not already been done. Lock the
daemon and make sure that a proper context has been setup in CTRL.
This function might also lock the daemon, which means that the
@@ -211,37 +303,19 @@ start_scd (ctrl_t ctrl)
if (opt.disable_scdaemon)
return gpg_error (GPG_ERR_NOT_SUPPORTED);
- /* If this is the first call for this session, setup the local data
- structure. */
- if (!ctrl->scd_local)
+ if (ctrl->scd_local && ctrl->scd_local->ctx)
{
- ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
- if (!ctrl->scd_local)
- return gpg_error_from_syserror ();
- ctrl->scd_local->ctrl_backlink = ctrl;
- ctrl->scd_local->next_local = scd_local_list;
- scd_local_list = ctrl->scd_local;
+ ctrl->scd_local->in_use = 1;
+ return 0; /* Okay, the context is fine. */
}
-
- /* Assert that the lock count is as expected. */
- if (ctrl->scd_local->locked)
+ if (ctrl->scd_local && ctrl->scd_local->in_use)
{
- log_error ("start_scd: invalid lock count (%d)\n",
- ctrl->scd_local->locked);
+ log_error ("start_scd: CTX is in use\n");
return gpg_error (GPG_ERR_INTERNAL);
}
- ctrl->scd_local->locked++;
-
- if (ctrl->scd_local->ctx)
- return 0; /* Okay, the context is fine. We used to test for an
- alive context here and do an disconnect. Now that we
- have a ticker function to check for it, it is easier
- not to check here but to let the connection run on an
- error instead. */
-
- /* We need to protect the following code. */
+ /* We need to serialize the access to scd_local_list and primary_scd_ctx. */
rc = npth_mutex_lock (&start_scd_lock);
if (rc)
{
@@ -250,6 +324,25 @@ start_scd (ctrl_t ctrl)
return gpg_error (GPG_ERR_INTERNAL);
}
+ /* If this is the first call for this session, setup the local data
+ structure. */
+ if (!ctrl->scd_local)
+ {
+ ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
+ if (!ctrl->scd_local)
+ {
+ err = gpg_error_from_syserror ();
+ rc = npth_mutex_unlock (&start_scd_lock);
+ if (rc)
+ log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
+ return err;
+ }
+ ctrl->scd_local->next_local = scd_local_list;
+ scd_local_list = ctrl->scd_local;
+ }
+
+ ctrl->scd_local->in_use = 1;
+
/* Check whether the pipe server has already been started and in
this case either reuse a lingering pipe connection or establish a
new socket based one. */
@@ -351,7 +444,7 @@ start_scd (ctrl_t ctrl)
detached flag so that under Windows SCDAEMON does not show up a
new window. */
rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv,
- no_close_list, atfork_cb, NULL,
+ no_close_list, atfork_cb, NULL,
ASSUAN_PIPE_CONNECT_DETACHED);
if (rc)
{
@@ -415,21 +508,41 @@ start_scd (ctrl_t ctrl)
primary_scd_ctx = ctx;
primary_scd_ctx_reusable = 0;
+ {
+ npth_t thread;
+ npth_attr_t tattr;
+ pid_t pid;
+
+ pid = assuan_get_pid (primary_scd_ctx);
+ err = npth_attr_init (&tattr);
+ if (!err)
+ {
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+ err = npth_create (&thread, &tattr, wait_child_thread,
+ (void *)(uintptr_t)pid);
+ if (err)
+ log_error ("error spawning wait_child_thread: %s\n", strerror (err));
+ npth_attr_destroy (&tattr);
+ }
+ }
+
leave:
+ rc = npth_mutex_unlock (&start_scd_lock);
+ if (rc)
+ log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
+
xfree (abs_homedir);
if (err)
{
unlock_scd (ctrl, err);
if (ctx)
- assuan_release (ctx);
+ assuan_release (ctx);
}
else
{
+ ctrl->scd_local->invalid = 0;
ctrl->scd_local->ctx = ctx;
}
- rc = npth_mutex_unlock (&start_scd_lock);
- if (rc)
- log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
return err;
}
@@ -444,144 +557,69 @@ agent_scd_check_running (void)
}
-/* Check whether the Scdaemon is still alive and clean it up if not. */
-void
-agent_scd_check_aliveness (void)
+/* Reset the SCD if it has been used. Actually it is not a reset but
+ a cleanup of resources used by the current connection. */
+int
+agent_reset_scd (ctrl_t ctrl)
{
- pid_t pid;
-#ifdef HAVE_W32_SYSTEM
- DWORD rc;
-#else
- int rc;
-#endif
- struct timespec abstime;
- int err;
-
- if (!primary_scd_ctx)
- return; /* No scdaemon running. */
+ int err = npth_mutex_lock (&start_scd_lock);
- /* This is not a critical function so we use a short timeout while
- acquiring the lock. */
- npth_clock_gettime (&abstime);
- abstime.tv_sec += 1;
- err = npth_mutex_timedlock (&start_scd_lock, &abstime);
if (err)
{
- if (err == ETIMEDOUT)
- {
- if (opt.verbose > 1)
- log_info ("failed to acquire the start_scd lock while"
- " doing an aliveness check: %s\n", strerror (err));
- }
- else
- log_error ("failed to acquire the start_scd lock while"
- " doing an aliveness check: %s\n", strerror (err));
- return;
+ log_error ("failed to acquire the start_scd lock: %s\n",
+ strerror (err));
}
-
- if (primary_scd_ctx)
+ else
{
- pid = assuan_get_pid (primary_scd_ctx);
-#ifdef HAVE_W32_SYSTEM
- /* If we have a PID we disconnect if either GetExitProcessCode
- fails or if ir returns the exit code of the scdaemon. 259 is
- the error code for STILL_ALIVE. */
- if (pid != (pid_t)(void*)(-1) && pid
- && (!GetExitCodeProcess ((HANDLE)pid, &rc) || rc != 259))
-#else
- if (pid != (pid_t)(-1) && pid
- && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) )
-#endif
+ if (ctrl->scd_local)
{
- /* Okay, scdaemon died. Disconnect the primary connection
- now but take care that it won't do another wait. Also
- cleanup all other connections and release their
- resources. The next use will start a new daemon then.
- Due to the use of the START_SCD_LOCAL we are sure that
- none of these context are actually in use. */
- struct scd_local_s *sl;
-
- assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1);
- assuan_release (primary_scd_ctx);
-
- for (sl=scd_local_list; sl; sl = sl->next_local)
+ if (ctrl->scd_local->ctx)
{
- if (sl->ctx)
+ /* We send a reset and keep that connection for reuse. */
+ if (ctrl->scd_local->ctx == primary_scd_ctx)
{
- if (sl->ctx != primary_scd_ctx)
- assuan_release (sl->ctx);
- sl->ctx = NULL;
+ /* Send a RESTART to the SCD. This is required for the
+ primary connection as a kind of virtual EOF; we don't
+ have another way to tell it that the next command
+ should be viewed as if a new connection has been
+ made. For the non-primary connections this is not
+ needed as we simply close the socket. We don't check
+ for an error here because the RESTART may fail for
+ example if the scdaemon has already been terminated.
+ Anyway, we need to set the reusable flag to make sure
+ that the aliveness check can clean it up. */
+ assuan_transact (primary_scd_ctx, "RESTART",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ primary_scd_ctx_reusable = 1;
}
+ else
+ assuan_release (ctrl->scd_local->ctx);
+ ctrl->scd_local->ctx = NULL;
}
- primary_scd_ctx = NULL;
- primary_scd_ctx_reusable = 0;
-
- xfree (socket_name);
- socket_name = NULL;
- }
- }
-
- err = npth_mutex_unlock (&start_scd_lock);
- if (err)
- log_error ("failed to release the start_scd lock while"
- " doing the aliveness check: %s\n", strerror (err));
-}
-
-
-
-/* Reset the SCD if it has been used. Actually it is not a reset but
- a cleanup of resources used by the current connection. */
-int
-agent_reset_scd (ctrl_t ctrl)
-{
- if (ctrl->scd_local)
- {
- if (ctrl->scd_local->ctx)
- {
- /* We can't disconnect the primary context because libassuan
- does a waitpid on it and thus the system would hang.
- Instead we send a reset and keep that connection for
- reuse. */
- if (ctrl->scd_local->ctx == primary_scd_ctx)
+ /* Remove the local context from our list and release it. */
+ if (!scd_local_list)
+ BUG ();
+ else if (scd_local_list == ctrl->scd_local)
+ scd_local_list = ctrl->scd_local->next_local;
+ else
{
- /* Send a RESTART to the SCD. This is required for the
- primary connection as a kind of virtual EOF; we don't
- have another way to tell it that the next command
- should be viewed as if a new connection has been
- made. For the non-primary connections this is not
- needed as we simply close the socket. We don't check
- for an error here because the RESTART may fail for
- example if the scdaemon has already been terminated.
- Anyway, we need to set the reusable flag to make sure
- that the aliveness check can clean it up. */
- assuan_transact (primary_scd_ctx, "RESTART",
- NULL, NULL, NULL, NULL, NULL, NULL);
- primary_scd_ctx_reusable = 1;
+ struct scd_local_s *sl;
+
+ for (sl=scd_local_list; sl->next_local; sl = sl->next_local)
+ if (sl->next_local == ctrl->scd_local)
+ break;
+ if (!sl->next_local)
+ BUG ();
+ sl->next_local = ctrl->scd_local->next_local;
}
- else
- assuan_release (ctrl->scd_local->ctx);
- ctrl->scd_local->ctx = NULL;
+ xfree (ctrl->scd_local);
+ ctrl->scd_local = NULL;
}
- /* Remove the local context from our list and release it. */
- if (!scd_local_list)
- BUG ();
- else if (scd_local_list == ctrl->scd_local)
- scd_local_list = ctrl->scd_local->next_local;
- else
- {
- struct scd_local_s *sl;
-
- for (sl=scd_local_list; sl->next_local; sl = sl->next_local)
- if (sl->next_local == ctrl->scd_local)
- break;
- if (!sl->next_local)
- BUG ();
- sl->next_local = ctrl->scd_local->next_local;
- }
- xfree (ctrl->scd_local);
- ctrl->scd_local = NULL;
+ err = npth_mutex_unlock (&start_scd_lock);
+ if (err)
+ log_error ("failed to release the start_scd lock: %s\n", strerror (err));
}
return 0;
@@ -1055,23 +1093,27 @@ inq_writekey_parms (void *opaque, const char *line)
}
-int
+/* Call scd to write a key to a card under the id KEYREF. */
+gpg_error_t
agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
- const char *id, const char *keydata, size_t keydatalen,
+ const char *keyref,
+ const char *keydata, size_t keydatalen,
int (*getpin_cb)(void *, const char *,
const char *, char*, size_t),
void *getpin_cb_arg)
{
- int rc;
+ gpg_error_t err;
char line[ASSUAN_LINELENGTH];
struct inq_needpin_parm_s parms;
- (void)serialno;
- rc = start_scd (ctrl);
- if (rc)
- return rc;
+ (void)serialno; /* NULL or a number to check for the correct card.
+ * But is is not implemented. */
- snprintf (line, DIM(line), "WRITEKEY %s%s", force ? "--force " : "", id);
+ err = start_scd (ctrl);
+ if (err)
+ return err;
+
+ snprintf (line, DIM(line), "WRITEKEY %s%s", force ? "--force " : "", keyref);
parms.ctx = ctrl->scd_local->ctx;
parms.getpin_cb = getpin_cb;
parms.getpin_cb_arg = getpin_cb_arg;
@@ -1080,9 +1122,9 @@ agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
parms.keydata = keydata;
parms.keydatalen = keydatalen;
- rc = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL,
- inq_writekey_parms, &parms, NULL, NULL);
- return unlock_scd (ctrl, rc);
+ err = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL,
+ inq_writekey_parms, &parms, NULL, NULL);
+ return unlock_scd (ctrl, err);
}
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index df63ed713..ebd28ab5a 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -195,9 +195,14 @@ struct ssh_key_type_spec
algorithm. */
ssh_signature_encoder_t signature_encoder;
- /* The name of the ECC curve or NULL. */
+ /* The name of the ECC curve or NULL for non-ECC algos. This is the
+ * canonical name for the curve as specified by RFC-5656. */
const char *curve_name;
+ /* An alias for curve_name or NULL. Actually this is Libcgrypt's
+ * primary name of the curve. */
+ const char *alt_curve_name;
+
/* The hash algorithm to be used with this key. 0 for using the
default. */
int hash_algo;
@@ -297,68 +302,71 @@ static const ssh_key_type_spec_t ssh_key_types[] =
{
"ssh-ed25519", "Ed25519", GCRY_PK_EDDSA, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_eddsa,
- "Ed25519", 0, SPEC_FLAG_IS_EdDSA
+ "Ed25519", NULL, 0, SPEC_FLAG_IS_EdDSA
},
{
"ssh-rsa", "RSA", GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu",
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
- NULL, 0, SPEC_FLAG_USE_PKCS1V2
+ NULL, NULL, 0, SPEC_FLAG_USE_PKCS1V2
},
{
"ssh-dss", "DSA", GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx",
NULL, ssh_signature_encoder_dsa,
- NULL, 0, 0
+ NULL, NULL, 0, 0
},
{
"ecdsa-sha2-nistp256", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA
+ "nistp256", "NIST P-256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA
},
{
"ecdsa-sha2-nistp384", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA
+ "nistp384", "NIST P-384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA
},
{
"ecdsa-sha2-nistp521", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
+ "nistp521", "NIST P-521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
},
{
"[email protected]", "Ed25519",
GCRY_PK_EDDSA, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_eddsa,
- "Ed25519", 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT
+ "Ed25519", NULL, 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT
},
{
GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu",
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
- NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT
+ NULL, NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT
},
{
GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx",
NULL, ssh_signature_encoder_dsa,
- NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT
+ NULL, NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT
},
{
"[email protected]", "ECDSA",
GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
+ "nistp256", "NIST P-256", GCRY_MD_SHA256,
+ SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
},
{
"[email protected]", "ECDSA",
GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
+ "nistp384", "NIST P-384", GCRY_MD_SHA384,
+ SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
},
{
"[email protected]", "ECDSA",
GCRY_PK_ECC, "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
- "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
+ "nistp521", "NIST P-521", GCRY_MD_SHA512,
+ SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
}
};
@@ -389,16 +397,24 @@ realloc_secure (void *a, size_t n)
/* Lookup the ssh-identifier for the ECC curve CURVE_NAME. Returns
- NULL if not found. */
+ * NULL if not found. If found the ssh indetifier is returned and a
+ * pointer to the canonical curve name as specified for ssh is stored
+ * at R_CANON_NAME. */
static const char *
-ssh_identifier_from_curve_name (const char *curve_name)
+ssh_identifier_from_curve_name (const char *curve_name,
+ const char **r_canon_name)
{
int i;
for (i = 0; i < DIM (ssh_key_types); i++)
if (ssh_key_types[i].curve_name
- && !strcmp (ssh_key_types[i].curve_name, curve_name))
- return ssh_key_types[i].ssh_identifier;
+ && (!strcmp (ssh_key_types[i].curve_name, curve_name)
+ || (ssh_key_types[i].alt_curve_name
+ && !strcmp (ssh_key_types[i].alt_curve_name, curve_name))))
+ {
+ *r_canon_name = ssh_key_types[i].curve_name;
+ return ssh_key_types[i].ssh_identifier;
+ }
return NULL;
}
@@ -1849,7 +1865,6 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
gpg_error_t err = 0;
gcry_sexp_t value_list = NULL;
gcry_sexp_t value_pair = NULL;
- char *curve_name = NULL;
estream_t stream = NULL;
void *blob = NULL;
size_t blob_size;
@@ -1867,7 +1882,7 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
goto out;
}
- /* Get the type of the key extpression. */
+ /* Get the type of the key expression. */
data = gcry_sexp_nth_data (sexp, 0, &datalen);
if (!data)
{
@@ -1898,49 +1913,17 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
/* Write the ssh algorithm identifier. */
if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
{
- /* Parse the "curve" parameter. We currently expect the curve
- name for ECC and not the parameters of the curve. This can
- easily be changed but then we need to find the curve name
- from the parameters using gcry_pk_get_curve. */
- const char *mapped;
- const char *sshname;
+ /* Map the curve name to the ssh name. */
+ const char *name, *sshname, *canon_name;
- gcry_sexp_release (value_pair);
- value_pair = gcry_sexp_find_token (value_list, "curve", 5);
- if (!value_pair)
- {
- err = gpg_error (GPG_ERR_INV_CURVE);
- goto out;
- }
- curve_name = gcry_sexp_nth_string (value_pair, 1);
- if (!curve_name)
- {
- err = gpg_error (GPG_ERR_INV_CURVE); /* (Or out of core.) */
- goto out;
- }
-
- /* Fixme: The mapping should be done by using gcry_pk_get_curve
- et al to iterate over all name aliases. */
- if (!strcmp (curve_name, "NIST P-256"))
- mapped = "nistp256";
- else if (!strcmp (curve_name, "NIST P-384"))
- mapped = "nistp384";
- else if (!strcmp (curve_name, "NIST P-521"))
- mapped = "nistp521";
- else
- mapped = NULL;
- if (mapped)
+ name = gcry_pk_get_curve (sexp, 0, NULL);
+ if (!name)
{
- xfree (curve_name);
- curve_name = xtrystrdup (mapped);
- if (!curve_name)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
+ err = gpg_error (GPG_ERR_INV_CURVE);
+ goto out;
}
- sshname = ssh_identifier_from_curve_name (curve_name);
+ sshname = ssh_identifier_from_curve_name (name, &canon_name);
if (!sshname)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
@@ -1949,7 +1932,7 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
err = stream_write_cstring (stream, sshname);
if (err)
goto out;
- err = stream_write_cstring (stream, curve_name);
+ err = stream_write_cstring (stream, canon_name);
if (err)
goto out;
}
@@ -2022,7 +2005,6 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
out:
gcry_sexp_release (value_list);
gcry_sexp_release (value_pair);
- xfree (curve_name);
es_fclose (stream);
es_free (blob);
@@ -2081,7 +2063,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
ssh_key_type_spec_t spec;
gcry_mpi_t *mpi_list = NULL;
const char *elems;
- char *curve_name = NULL;
+ const char *curve_name = NULL;
err = stream_read_cstring (stream, &key_type);
@@ -2204,34 +2186,19 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
* certificate.
*/
unsigned char *buffer;
- const char *mapped;
err = stream_read_string (cert? cert : stream, 0, &buffer, NULL);
if (err)
goto out;
- curve_name = buffer;
- /* Fixme: Check that curve_name matches the keytype. */
- /* Because Libgcrypt < 1.6 has no support for the "nistpNNN"
- curve names, we need to translate them here to Libgcrypt's
- native names. */
- if (!strcmp (curve_name, "nistp256"))
- mapped = "NIST P-256";
- else if (!strcmp (curve_name, "nistp384"))
- mapped = "NIST P-384";
- else if (!strcmp (curve_name, "nistp521"))
- mapped = "NIST P-521";
- else
- mapped = NULL;
- if (mapped)
+ /* Get the canonical name. Should be the same as the read
+ * string but we use this mapping to validate that name. */
+ if (!ssh_identifier_from_curve_name (buffer, &curve_name))
{
- xfree (curve_name);
- curve_name = xtrystrdup (mapped);
- if (!curve_name)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
+ err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ xfree (buffer);
+ goto out;
}
+ xfree (buffer);
err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
if (err)
@@ -2299,7 +2266,6 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
out:
es_fclose (cert);
mpint_list_free (mpi_list);
- xfree (curve_name);
xfree (key_type);
xfree (comment);
@@ -2647,6 +2613,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
continue;
err = ssh_send_key_public (key_blobs, key_public, cardsn);
+ if (err && opt.verbose)
+ gcry_log_debugsxp ("pubkey", key_public);
gcry_sexp_release (key_public);
key_public = NULL;
xfree (cardsn);
@@ -2722,6 +2690,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
}
else
{
+ log_error ("ssh request identities failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
}
@@ -2751,7 +2721,7 @@ data_hash (unsigned char *data, size_t data_n,
allow the use of signature algorithms that implement the hashing
internally (e.g. Ed25519). On success the created signature is
stored in ssh format at R_SIG and it's size at R_SIGLEN; the caller
- must use es_free to releaase this memory. */
+ must use es_free to release this memory. */
static gpg_error_t
data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
const void *hash, size_t hashlen,
@@ -3249,9 +3219,10 @@ ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
while (1)
{
err = stream_read_byte (request, &b);
- if (gpg_err_code (err) == GPG_ERR_EOF)
- {
- err = 0;
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
break;
}
@@ -3625,7 +3596,7 @@ static void
get_client_info (int fd, struct peer_info_s *out)
{
pid_t client_pid = (pid_t)(-1);
- uid_t client_uid = (uid_t)-1;
+ int client_uid = -1;
#ifdef SO_PEERCRED
{
@@ -3640,10 +3611,10 @@ get_client_info (int fd, struct peer_info_s *out)
{
#if defined (HAVE_STRUCT_SOCKPEERCRED_PID) || defined (HAVE_STRUCT_UCRED_PID)
client_pid = cr.pid;
- client_uid = cr.uid;
+ client_uid = (int)cr.uid;
#elif defined (HAVE_STRUCT_UCRED_CR_PID)
client_pid = cr.cr_pid;
- client_pid = cr.cr_uid;
+ client_uid = (int)cr.cr_uid;
#else
#error "Unknown SO_PEERCRED struct"
#endif
@@ -3660,7 +3631,7 @@ get_client_info (int fd, struct peer_info_s *out)
len = sizeof (struct xucred);
if (!getsockopt (fd, SOL_LOCAL, LOCAL_PEERCRED, &cr, &len))
- client_uid = cr.cr_uid;
+ client_uid = (int)cr.cr_uid;
}
#endif
}
@@ -3670,8 +3641,10 @@ get_client_info (int fd, struct peer_info_s *out)
socklen_t unpl = sizeof unp;
if (getsockopt (fd, 0, LOCAL_PEEREID, &unp, &unpl) != -1)
- client_pid = unp.unp_pid;
- client_uid = unp.unp_euid;
+ {
+ client_pid = unp.unp_pid;
+ client_uid = (int)unp.unp_euid;
+ }
}
#elif defined (HAVE_GETPEERUCRED)
{
@@ -3680,7 +3653,7 @@ get_client_info (int fd, struct peer_info_s *out)
if (getpeerucred (fd, &ucred) != -1)
{
client_pid = ucred_getpid (ucred);
- client_uid = ucred_geteuid (ucred);
+ client_uid = (int)ucred_geteuid (ucred);
ucred_free (ucred);
}
}
@@ -3689,7 +3662,7 @@ get_client_info (int fd, struct peer_info_s *out)
#endif
out->pid = (client_pid == (pid_t)(-1)? 0 : (unsigned long)client_pid);
- out->uid = (int)client_uid;
+ out->uid = client_uid;
}
diff --git a/agent/command.c b/agent/command.c
index 925d1f780..5e2b6df2b 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -887,7 +887,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
int no_protection;
- unsigned char *value;
+ unsigned char *value = NULL;
size_t valuelen;
unsigned char *newpasswd = NULL;
membuf_t outbuf;
@@ -1595,19 +1595,24 @@ static const char hlp_clear_passphrase[] =
"may be used to invalidate the cache entry for a passphrase. The\n"
"function returns with OK even when there is no cached passphrase.\n"
"The --mode=normal option is used to clear an entry for a cacheid\n"
- "added by the agent.\n";
+ "added by the agent. The --mode=ssh option is used for a cacheid\n"
+ "added for ssh.\n";
static gpg_error_t
cmd_clear_passphrase (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
char *cacheid = NULL;
char *p;
- int opt_normal;
+ cache_mode_t cache_mode = CACHE_MODE_USER;
if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
- opt_normal = has_option (line, "--mode=normal");
+ if (has_option (line, "--mode=normal"))
+ cache_mode = CACHE_MODE_NORMAL;
+ else if (has_option (line, "--mode=ssh"))
+ cache_mode = CACHE_MODE_SSH;
+
line = skip_options (line);
/* parse the stuff */
@@ -1620,12 +1625,9 @@ cmd_clear_passphrase (assuan_context_t ctx, char *line)
if (!*cacheid || strlen (cacheid) > 50)
return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of cacheID");
- agent_put_cache (ctrl, cacheid,
- opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER,
- NULL, 0);
+ agent_put_cache (ctrl, cacheid, cache_mode, NULL, 0);
- agent_clear_passphrase (ctrl, cacheid,
- opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER);
+ agent_clear_passphrase (ctrl, cacheid, cache_mode);
return 0;
}
@@ -2482,19 +2484,23 @@ cmd_delete_key (assuan_context_t ctx, char *line)
#endif
static const char hlp_keytocard[] =
- "KEYTOCARD [--force] <hexstring_with_keygrip> <serialno> <id> <timestamp>\n"
- "\n";
+ "KEYTOCARD [--force] <hexgrip> <serialno> <keyref> [<timestamp>]\n"
+ "\n"
+ "TIMESTAMP is required for OpenPGP and defaults to the Epoch. The\n"
+ "SERIALNO is used for checking; use \"-\" to disable the check.";
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;
+ char *argv[5];
+ int argc;
unsigned char grip[20];
+ const char *serialno, *timestamp_str, *keyref;
gcry_sexp_t s_skey = NULL;
unsigned char *keydata;
size_t keydatalen;
- const char *serialno, *timestamp_str, *id;
unsigned char *shadow_info = NULL;
time_t timestamp;
@@ -2504,7 +2510,14 @@ cmd_keytocard (assuan_context_t ctx, char *line)
force = has_option (line, "--force");
line = skip_options (line);
- err = parse_keygrip (ctx, line, grip);
+ argc = split_fields (line, argv, DIM (argv));
+ if (argc < 3)
+ {
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+
+ err = parse_keygrip (ctx, argv[0], grip);
if (err)
goto leave;
@@ -2514,39 +2527,19 @@ cmd_keytocard (assuan_context_t ctx, char *line)
goto leave;
}
- /* Fixme: Replace the parsing code by split_fields(). */
- line += 40;
- while (*line && (*line == ' ' || *line == '\t'))
- line++;
- serialno = line;
- while (*line && (*line != ' ' && *line != '\t'))
- line++;
- if (!*line)
- {
- err = gpg_error (GPG_ERR_MISSING_VALUE);
- goto leave;
- }
- *line = '\0';
- line++;
- while (*line && (*line == ' ' || *line == '\t'))
- line++;
- id = line;
- while (*line && (*line != ' ' && *line != '\t'))
- line++;
- if (!*line)
- {
- err = gpg_error (GPG_ERR_MISSING_VALUE);
- goto leave;
- }
- *line = '\0';
- line++;
- while (*line && (*line == ' ' || *line == '\t'))
- line++;
- timestamp_str = line;
- while (*line && (*line != ' ' && *line != '\t'))
- line++;
- if (*line)
- *line = '\0';
+ /* Note that checking of the s/n is currently not implemented but we
+ * want to provide a clean interface if we ever implement it. */
+ serialno = argv[1];
+ if (!strcmp (serialno, "-"))
+ serialno = NULL;
+
+ keyref = argv[2];
+
+ /* FIXME: Default to the creation time as stored in the private
+ * key. The parameter is here so that gpg can make sure that the
+ * timestamp as used for key creation (and thus the openPGP
+ * fingerprint) is used. */
+ timestamp_str = argc > 3? argv[3] : "19700101T000000";
if ((timestamp = isotime2epoch (timestamp_str)) == (time_t)(-1))
{
@@ -2558,38 +2551,37 @@ cmd_keytocard (assuan_context_t ctx, char *line)
&shadow_info, CACHE_MODE_IGNORE, NULL,
&s_skey, NULL);
if (err)
- {
- xfree (shadow_info);
- goto leave;
- }
+ goto leave;
if (shadow_info)
{
- /* Key is on a smartcard already. */
- xfree (shadow_info);
- gcry_sexp_release (s_skey);
+ /* Key is already on a smartcard - we can't extract it. */
err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
goto leave;
}
- keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
+ /* Note: We can't use make_canon_sexp because we need to allocate a
+ * few extra bytes for our hack below. */
+ keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
keydata = xtrymalloc_secure (keydatalen + 30);
if (keydata == NULL)
{
err = gpg_error_from_syserror ();
- gcry_sexp_release (s_skey);
goto leave;
}
-
gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, keydata, keydatalen);
gcry_sexp_release (s_skey);
+ s_skey = NULL;
keydatalen--; /* Decrement for last '\0'. */
- /* Add timestamp "created-at" in the private key */
+ /* Hack to insert the timestamp "created-at" into the private key. */
snprintf (keydata+keydatalen-1, 30, KEYTOCARD_TIMESTAMP_FORMAT, timestamp);
keydatalen += 10 + 19 - 1;
- err = divert_writekey (ctrl, force, serialno, id, keydata, keydatalen);
+
+ err = divert_writekey (ctrl, force, serialno, keyref, keydata, keydatalen);
xfree (keydata);
leave:
+ gcry_sexp_release (s_skey);
+ xfree (shadow_info);
return leave_cmd (ctx, err);
}
@@ -2751,7 +2743,7 @@ cmd_put_secret (assuan_context_t ctx, char *line)
* into a string. Instead of resorting to base64 encoding we use a
* special percent escaping which only quoted the Nul and the
* percent character. */
- string = percent_data_escape (value? value : valstr, valuelen);
+ string = percent_data_escape (0, NULL, value? value : valstr, valuelen);
if (!string)
{
err = gpg_error_from_syserror ();
@@ -3588,8 +3580,13 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
}
else
{
+#ifdef HAVE_W32_SYSTEM
+ pid = assuan_get_pid (ctx);
+ ctrl->client_uid = -1;
+#else
pid = client_creds->pid;
ctrl->client_uid = client_creds->uid;
+#endif
}
ctrl->client_pid = (pid == ASSUAN_INVALID_PID)? 0 : (unsigned long)pid;
ctrl->server_local->connect_from_self = (pid == getpid ());
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index b85b490c1..e89c74a19 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -195,7 +195,7 @@ has_percent0A_suffix (const char *string)
string with the passphrase, the buffer may optionally be padded
with arbitrary characters.
- If DESC_TEXT is not NULL it can be used as further informtion shown
+ If DESC_TEXT is not NULL it can be used as further information shown
atop of the INFO message.
INFO gets displayed as part of a generic string. However if the
@@ -278,25 +278,47 @@ getpin_cb (void *opaque, const char *desc_text, const char *info,
{
if (info)
{
- char *desc, *desc2;
+ char *desc;
+ const char *desc2;
- if ( asprintf (&desc,
- L_("%s%%0A%%0AUse the reader's pinpad for input."),
- info) < 0 )
- rc = gpg_error_from_syserror ();
+ if (!strcmp (info, "--ack"))
+ {
+ desc2 = L_("Push ACK button on card/token.");
+
+ if (desc_text)
+ {
+ desc = strconcat (desc_text,
+ has_percent0A_suffix (desc_text)
+ ? "%0A" : "%0A%0A",
+ desc2, NULL);
+ desc2 = NULL;
+ }
+ else
+ desc = NULL;
+ }
else
{
- /* Prepend DESC_TEXT to INFO. */
+ desc2 = NULL;
+
if (desc_text)
- desc2 = strconcat (desc_text,
- has_percent0A_suffix (desc_text)
- ? "%0A" : "%0A%0A",
- desc, NULL);
+ desc = strconcat (desc_text,
+ has_percent0A_suffix (desc_text)
+ ? "%0A" : "%0A%0A",
+ info, "%0A%0A",
+ L_("Use the reader's pinpad for input."),
+ NULL);
else
- desc2 = NULL;
+ desc = strconcat (info, "%0A%0A",
+ L_("Use the reader's pinpad for input."),
+ NULL);
+ }
+
+ if (!desc2 && !desc)
+ rc = gpg_error_from_syserror ();
+ else
+ {
rc = agent_popup_message_start (ctrl,
desc2? desc2:desc, NULL);
- xfree (desc2);
xfree (desc);
}
}
@@ -476,6 +498,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
char *kid;
const unsigned char *s;
size_t n;
+ int depth;
const unsigned char *ciphertext;
size_t ciphertextlen;
char *plaintext;
@@ -484,7 +507,6 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
(void)desc_text;
*r_padding = -1;
-
s = cipher;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
@@ -500,6 +522,21 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
+
+ /* First check whether we have a flags parameter and skip it. */
+ if (smatch (&s, n, "flags"))
+ {
+ depth = 1;
+ if (sskip (&s, &depth) || depth)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ if (*s != '(')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++;
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+
if (smatch (&s, n, "rsa"))
{
if (*s != '(')
@@ -560,12 +597,13 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
return rc;
}
-int
+
+gpg_error_t
divert_writekey (ctrl_t ctrl, int force, const char *serialno,
- const char *id, const char *keydata, size_t keydatalen)
+ const char *keyref, const char *keydata, size_t keydatalen)
{
- return agent_card_writekey (ctrl, force, serialno, id, keydata, keydatalen,
- getpin_cb, ctrl);
+ return agent_card_writekey (ctrl, force, serialno, keyref,
+ keydata, keydatalen, getpin_cb, ctrl);
}
int
diff --git a/agent/findkey.c b/agent/findkey.c
index 78c3b1a47..89a18fa9e 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -632,7 +632,17 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
pi->check_cb_arg = &arg;
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, hexgrip, cache_mode);
- if (!rc)
+ if (rc)
+ {
+ if ((pi->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
+ {
+ log_error ("Clearing pinentry cache which caused error %s\n",
+ gpg_strerror (rc));
+
+ agent_clear_passphrase (ctrl, hexgrip, cache_mode);
+ }
+ }
+ else
{
assert (arg.unprotected_key);
if (arg.change_required)
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 1fdc94d0f..d9e2bbf25 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -112,6 +112,7 @@ enum cmd_and_opt_values
oCheckPassphrasePattern,
oMaxPassphraseDays,
oEnablePassphraseHistory,
+ oDisableExtendedKeyFormat,
oEnableExtendedKeyFormat,
oUseStandardSocket,
oNoUseStandardSocket,
@@ -135,10 +136,13 @@ enum cmd_and_opt_values
oDisableScdaemon,
oDisableCheckOwnSocket,
oS2KCount,
+ oS2KCalibration,
oAutoExpandSecmem,
oListenBacklog,
- oWriteEnvFile
+ oWriteEnvFile,
+
+ oNoop
};
@@ -250,9 +254,11 @@ static ARGPARSE_OPTS opts[] = {
/* */ "@"
#endif
),
+ ARGPARSE_s_n (oDisableExtendedKeyFormat, "disable-extended-key-format", "@"),
ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"),
ARGPARSE_s_u (oS2KCount, "s2k-count", "@"),
+ ARGPARSE_s_u (oS2KCalibration, "s2k-calibration", "@"),
ARGPARSE_op_u (oAutoExpandSecmem, "auto-expand-secmem", "@"),
@@ -263,6 +269,9 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"),
+ /* Dummy options. */
+
+
ARGPARSE_end () /* End of list */
};
@@ -823,7 +832,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
opt.check_passphrase_pattern = NULL;
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
opt.enable_passphrase_history = 0;
- opt.enable_extended_key_format = 0;
+ opt.enable_extended_key_format = 1;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 1;
opt.allow_external_cache = 1;
@@ -834,6 +843,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
/* Note: When changing the next line, change also gpgconf_list. */
opt.ssh_fingerprint_digest = GCRY_MD_MD5;
opt.s2k_count = 0;
+ set_s2k_calibration_time (0); /* Set to default. */
return 1;
}
@@ -851,7 +861,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
case oLogFile:
if (!reread)
- return 0; /* not handeld */
+ return 0; /* not handled */
if (!current_logfile || !pargs->r.ret_str
|| strcmp (current_logfile, pargs->r.ret_str))
{
@@ -898,7 +908,11 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
break;
case oEnableExtendedKeyFormat:
- opt.enable_extended_key_format = 1;
+ opt.enable_extended_key_format = 2;
+ break;
+ case oDisableExtendedKeyFormat:
+ if (opt.enable_extended_key_format != 2)
+ opt.enable_extended_key_format = 0;
break;
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
@@ -929,6 +943,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
opt.s2k_count = pargs->r.ret_ulong;
break;
+ case oS2KCalibration:
+ set_s2k_calibration_time (pargs->r.ret_ulong);
+ break;
+
+ case oNoop: break;
+
default:
return 0; /* not handled */
}
@@ -1444,8 +1464,6 @@ main (int argc, char **argv )
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("pinentry-timeout:%lu:0:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
- es_printf ("enable-extended-key-format:%lu:\n",
- GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
@@ -1768,7 +1786,7 @@ main (int argc, char **argv )
/* Unless we are running with a program given on the command
* line we can assume that the inotify things works and thus
- * we can avoid tye regular stat calls. */
+ * we can avoid the regular stat calls. */
if (!argc)
reliable_homedir_inotify = 1;
}
@@ -2108,7 +2126,7 @@ get_agent_scd_notify_event (void)
GetCurrentProcess(), &h2,
EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
{
- log_error ("setting syncronize for scd notify event failed: %s\n",
+ log_error ("setting synchronize for scd notify event failed: %s\n",
w32_strerror (-1) );
CloseHandle (h);
}
@@ -2370,9 +2388,6 @@ handle_tick (void)
if (!last_minute)
last_minute = time (NULL);
- /* Check whether the scdaemon has died and cleanup in this case. */
- agent_scd_check_aliveness ();
-
/* If we are running as a child of another process, check whether
the parent is still alive and shutdown if not. */
#ifndef HAVE_W32_SYSTEM
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 2e48b346e..c7426db9d 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -234,7 +234,7 @@ The currently defined protection modes are:
(csum n)
(protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)))
- Note that the public key paramaters in SKEY are duplicated and
+ Note that the public key parameters in SKEY are duplicated and
should be identical to their copies in the standard parameter
elements. Here is an example of an entire protected private key
using this format:
@@ -359,8 +359,8 @@ KEY_1 to KEY_N are unique identifiers for the shared secret, for
example an URI. In case this information should be kept confidential
as well, they may not appear in the unprotected part; however they are
mandatory in the encrypted_octet_string. The list of keywords is
-optional. The oder of the "key" lists and the order of the "value"
-lists mut match, that is the first "key"-list is associated with the
+optional. The order of the "key" lists and the order of the "value"
+lists must match, that is the first "key"-list is associated with the
first "value" list in the encrypted_octet_string.
The protection mode etc. is identical to the protection mode as
diff --git a/agent/learncard.c b/agent/learncard.c
index abe1dd0bf..f3219ed8f 100644
--- a/agent/learncard.c
+++ b/agent/learncard.c
@@ -40,7 +40,7 @@ struct keypair_info_s
char hexgrip[1]; /* The keygrip (i.e. a hash over the public key
parameters) formatted as a hex string.
Allocated somewhat large to also act as
- memeory for the above ID field. */
+ memory for the above ID field. */
};
typedef struct keypair_info_s *KEYPAIR_INFO;
diff --git a/agent/pksign.c b/agent/pksign.c
index f54af0817..828e63f58 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -367,20 +367,29 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
if (is_RSA)
{
+ unsigned char *p = buf;
+
check_signature = 1;
- if (*buf & 0x80)
+
+ /*
+ * Smartcard returns fixed-size data, which is good for
+ * PKCS1. If variable-size unsigned MPI is needed, remove
+ * zeros.
+ */
+ if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1
+ || ctrl->digest.raw_value)
{
- len++;
- buf = xtryrealloc (buf, len);
- if (!buf)
- goto leave;
+ int i;
- memmove (buf + 1, buf, len - 1);
- *buf = 0;
+ for (i = 0; i < len - 1; i++)
+ if (p[i])
+ break;
+ p += i;
+ len -= i;
}
err = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))",
- (int)len, buf);
+ (int)len, p);
}
else if (is_EdDSA)
{
@@ -389,53 +398,34 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
}
else if (is_ECDSA)
{
- unsigned char *r_buf_allocated = NULL;
- unsigned char *s_buf_allocated = NULL;
unsigned char *r_buf, *s_buf;
int r_buflen, s_buflen;
+ int i;
r_buflen = s_buflen = len/2;
- if (*buf & 0x80)
- {
- r_buflen++;
- r_buf_allocated = xtrymalloc (r_buflen);
- if (!r_buf_allocated)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
-
- r_buf = r_buf_allocated;
- memcpy (r_buf + 1, buf, len/2);
- *r_buf = 0;
- }
- else
- r_buf = buf;
-
- if (*(buf + len/2) & 0x80)
- {
- s_buflen++;
- s_buf_allocated = xtrymalloc (s_buflen);
- if (!s_buf_allocated)
- {
- err = gpg_error_from_syserror ();
- xfree (r_buf_allocated);
- goto leave;
- }
-
- s_buf = s_buf_allocated;
- memcpy (s_buf + 1, buf + len/2, len/2);
- *s_buf = 0;
- }
- else
- s_buf = buf + len/2;
+ /*
+ * Smartcard returns fixed-size data. For ECDSA signature,
+ * variable-size unsigned MPI is assumed, thus, remove
+ * zeros.
+ */
+ r_buf = buf;
+ for (i = 0; i < r_buflen - 1; i++)
+ if (r_buf[i])
+ break;
+ r_buf += i;
+ r_buflen -= i;
+
+ s_buf = buf + len/2;
+ for (i = 0; i < s_buflen - 1; i++)
+ if (s_buf[i])
+ break;
+ s_buf += i;
+ s_buflen -= i;
err = gcry_sexp_build (&s_sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))",
r_buflen, r_buf,
s_buflen, s_buf);
- xfree (r_buf_allocated);
- xfree (s_buf_allocated);
}
else
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
diff --git a/agent/protect.c b/agent/protect.c
index 16ae715e1..61fb8f45d 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -41,6 +41,7 @@
#include "cvt-openpgp.h"
#include "../common/sexp-parse.h"
+#include "../common/openpgpdefs.h" /* For s2k functions. */
/* The protection mode for encryption. The supported modes for
@@ -49,9 +50,6 @@
#define PROT_CIPHER_STRING "aes"
#define PROT_CIPHER_KEYLEN (128/8)
-/* Decode an rfc4880 encoded S2K count. */
-#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6))
-
/* A table containing the information needed to create a protected
private key. */
@@ -71,6 +69,13 @@ static const struct {
};
+/* The number of milliseconds we use in the S2K function and the
+ * calibrated count value. A count value of zero indicates that the
+ * calibration has not yet been done or needs to be done again. */
+static unsigned int s2k_calibration_time = AGENT_S2K_CALIBRATION;
+static unsigned long s2k_calibrated_count;
+
+
/* A helper object for time measurement. */
struct calibrate_time_s
{
@@ -175,11 +180,11 @@ calibrate_s2k_count (void)
ms = calibrate_s2k_count_one (count);
if (opt.verbose > 1)
log_info ("S2K calibration: %lu -> %lums\n", count, ms);
- if (ms > AGENT_S2K_CALIBRATION)
+ if (ms > s2k_calibration_time)
break;
}
- count = (unsigned long)(((double)count / ms) * AGENT_S2K_CALIBRATION);
+ count = (unsigned long)(((double)count / ms) * s2k_calibration_time);
count /= 1024;
count *= 1024;
if (count < 65536)
@@ -195,18 +200,30 @@ calibrate_s2k_count (void)
}
+/* Set the calibration time. This may be called early at startup or
+ * at any time. Thus it should one set variables. */
+void
+set_s2k_calibration_time (unsigned int milliseconds)
+{
+ if (!milliseconds)
+ milliseconds = AGENT_S2K_CALIBRATION;
+ else if (milliseconds > 60 * 1000)
+ milliseconds = 60 * 1000; /* Cap at 60 seconds. */
+ s2k_calibration_time = milliseconds;
+ s2k_calibrated_count = 0; /* Force re-calibration. */
+}
+
+
/* Return the calibrated S2K count. This is only public for the use
* of the Assuan getinfo s2k_count_cal command. */
unsigned long
get_calibrated_s2k_count (void)
{
- static unsigned long count;
-
- if (!count)
- count = calibrate_s2k_count ();
+ if (!s2k_calibrated_count)
+ s2k_calibrated_count = calibrate_s2k_count ();
/* Enforce a lower limit. */
- return count < 65536 ? 65536 : count;
+ return s2k_calibrated_count < 65536 ? 65536 : s2k_calibrated_count;
}
@@ -606,7 +623,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
int have_curve = 0;
if (use_ocb == -1)
- use_ocb = opt.enable_extended_key_format;
+ use_ocb = !!opt.enable_extended_key_format;
/* Create an S-expression with the protected-at timestamp. */
memcpy (timestamp_exp, "(12:protected-at15:", 19);
@@ -1109,7 +1126,7 @@ agent_unprotect (ctrl_t ctrl,
if (!protect_info[infidx].algo)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
- /* See wether we have a protected-at timestamp. */
+ /* See whether we have a protected-at timestamp. */
protect_list = s; /* Save for later. */
if (protected_at)
{