aboutsummaryrefslogtreecommitdiffstats
path: root/g10/cardglue.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/cardglue.c')
-rw-r--r--g10/cardglue.c418
1 files changed, 349 insertions, 69 deletions
diff --git a/g10/cardglue.c b/g10/cardglue.c
index 9e7aef81f..940ec64a3 100644
--- a/g10/cardglue.c
+++ b/g10/cardglue.c
@@ -43,9 +43,16 @@
#include "apdu.h"
#include "app-common.h"
+/* If we build w/o agent support, assuan.h won't be included and thus
+ we need to define a repalcement for the assuan error type. */
+#ifndef ENABLE_AGENT_SUPPORT
+typedef int assuan_error_t;
+#endif
+
+
struct ctrl_ctx_s
{
- int (*status_cb)(void *opaque, const char *line);
+ assuan_error_t (*status_cb)(void *opaque, const char *line);
void *status_cb_arg;
};
@@ -57,10 +64,14 @@ struct pincb_parm_s
static char *default_reader_port;
-static APP current_app;
+static app_t current_app;
+/* Local prototypes. */
+static assuan_error_t learn_status_cb (void *opaque, const char *line);
+
+
/* Create a serialno/fpr string from the serial number and the secret
key. caller must free the returned string. There is no error
return. [Taken from 1.9's keyid.c]*/
@@ -135,8 +146,12 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
}
-void gcry_md_hash_buffer (int algo, void *digest,
- const void *buffer, size_t length)
+/* Replacement function of the Libgcrypt onewhich is used in gnupg
+ 1.9. Thus function computes the digest of ALGO from the data in
+ BUFFER of LENGTH. ALGO must be supported. */
+void
+gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length)
{
MD_HANDLE h = md_open (algo, 0);
if (!h)
@@ -204,7 +219,7 @@ card_set_reader_port (const char *portstr)
no update time is available the returned value is 0. Caller must
free SERIAL unless the function returns an error. */
int
-app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
+app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
{
unsigned char *buf, *p;
int i;
@@ -254,19 +269,91 @@ agent_release_card_info (struct agent_card_info_s *info)
}
+/* Print an error message for a failed assuan-Transact and return a
+ gpg error code. No error is printed if RC is 0. */
+static gpg_error_t
+test_transact (int rc, const char *command)
+{
+ if (!rc)
+ return 0;
+ log_error ("sending command `%s' to agent failed: %s\n",
+ command, assuan_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+}
+
+
+/* Try to open a card using an already running agent. Prepare a
+ proper application context and return it. */
+static app_t
+open_card_via_agent (int *scd_available)
+{
+ assuan_context_t ctx;
+ app_t app;
+ struct agent_card_info_s info;
+ int rc;
+
+ *scd_available = 0;
+ ctx = agent_open (1);
+ if (!ctx)
+ return NULL;
+
+ /* Request the serialbnumber of the card. If we get
+ NOT_SUPPORTED or NO_SCDAEMON back, the gpg-agent either has
+ disabled scdaemon or it can't be used. We close the connection
+ in this case and use our own code. This may happen if just the
+ gpg-agent has been installed for the sake of passphrase
+ caching. */
+ memset (&info, 0, sizeof info);
+ rc = assuan_transact (ctx, "SCD SERIALNO openpgp",
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, &info);
+ if (rc)
+ {
+ if ((rc & 0xffff) == 60 || (rc & 0xffff) == 119)
+ ; /* No scdaemon available to gpg-agent. */
+ else
+ {
+ write_status_text (STATUS_CARDCTRL, "4");
+ log_info ("selecting openpgp failed: %s\n", assuan_strerror (rc));
+ *scd_available = 1;
+ }
+ agent_release_card_info (&info);
+ agent_close (ctx);
+ return NULL;
+ }
+
+ app = xcalloc (1, sizeof *app);
+ app->assuan_ctx = ctx;
+
+ return app;
+}
+
+
+
/* Open the current card and select the openpgp application. Return
an APP context handle to be used for further procesing or NULL on
error or if no OpenPGP application exists.*/
-static APP
+static app_t
open_card (void)
{
int slot = -1;
int rc;
- APP app;
+ app_t app;
int did_shutdown = 0;
+ int scd_available;
+
+ /* First check whether we can contact a gpg-agent and divert all
+ operation to it. This is required because gpg as well as the
+ agent require exclusive access to the reader. */
+ app = open_card_via_agent (&scd_available);
+ if (app)
+ goto ready; /* Yes, there is a agent with a usable card, go that way. */
+ if (scd_available)
+ return NULL; /* agent avilabale but card problem. */
- card_close ();
+ /* No agent or usable agent, thus we do it on our own. */
+ card_close ();
retry:
if (did_shutdown)
@@ -309,6 +396,7 @@ open_card (void)
return NULL;
}
+ ready:
app->initialized = 1;
current_app = app;
if (is_status_enabled () )
@@ -327,15 +415,19 @@ open_card (void)
return app;
}
+
void
card_close (void)
{
if (current_app)
{
- APP app = current_app;
+ app_t app = current_app;
current_app = NULL;
- apdu_close_reader (app->slot);
+ if (app->assuan_ctx)
+ agent_close (app->assuan_ctx);
+ else
+ apdu_close_reader (app->slot);
xfree (app);
}
}
@@ -382,7 +474,7 @@ format_cacheid (const char *sn)
card context will be closed in all cases except for 0 as return
value and if it was possible to merely shutdown the reader. */
static int
-check_card_serialno (APP app, const char *serialno)
+check_card_serialno (app_t app, const char *serialno)
{
const char *s;
int ask = 0;
@@ -505,7 +597,7 @@ store_serialno (const char *line)
-static int
+static assuan_error_t
learn_status_cb (void *opaque, const char *line)
{
struct agent_card_info_s *parm = opaque;
@@ -644,7 +736,7 @@ learn_status_cb (void *opaque, const char *line)
int
agent_learn (struct agent_card_info_s *info)
{
- APP app;
+ app_t app;
int rc;
struct ctrl_ctx_s ctrl;
time_t stamp;
@@ -655,35 +747,68 @@ agent_learn (struct agent_card_info_s *info)
return gpg_error (GPG_ERR_CARD);
memset (info, 0, sizeof *info);
- memset (&ctrl, 0, sizeof ctrl);
- ctrl.status_cb = learn_status_cb;
- ctrl.status_cb_arg = info;
- rc = app_get_serial_and_stamp (app, &serial, &stamp);
- if (!rc)
+ if (app->assuan_ctx)
{
- send_status_info (&ctrl, "SERIALNO", serial, strlen(serial), NULL, 0);
- xfree (serial);
- rc = app->fnc.learn_status (app, &ctrl);
+ rc = assuan_transact (app->assuan_ctx, "SCD LEARN --force",
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, info);
+ rc = test_transact (rc, "SCD LEARN");
+ }
+ else
+ {
+ memset (&ctrl, 0, sizeof ctrl);
+ ctrl.status_cb = learn_status_cb;
+ ctrl.status_cb_arg = info;
+
+ rc = app_get_serial_and_stamp (app, &serial, &stamp);
+ if (!rc)
+ {
+ send_status_info (&ctrl, "SERIALNO",
+ serial, strlen(serial), NULL, 0);
+ xfree (serial);
+ rc = app->fnc.learn_status (app, &ctrl);
+ }
}
return rc;
}
-/* Get an attribite from the card. Make sure info is initialized. */
+
+/* Get an attribute from the card. Make sure info is initialized. */
int
agent_scd_getattr (const char *name, struct agent_card_info_s *info)
{
- APP app;
+ int rc;
+ app_t app;
struct ctrl_ctx_s ctrl;
app = current_app? current_app : open_card ();
if (!app)
return gpg_error (GPG_ERR_CARD);
- ctrl.status_cb = learn_status_cb;
- ctrl.status_cb_arg = info;
- return app->fnc.getattr (app, &ctrl, name);
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+
+ /* We assume that NAME does not need escaping. */
+ if (12 + strlen (name) > DIM(line)-1)
+ return gpg_error (GPG_ERR_CARD);
+ stpcpy (stpcpy (line, "SCD GETATTR "), name);
+
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, info),
+ "SCD GETATTR");
+ }
+ else
+ {
+ ctrl.status_cb = learn_status_cb;
+ ctrl.status_cb_arg = info;
+ rc = app->fnc.getattr (app, &ctrl, name);
+ }
+
+ return rc;
}
@@ -797,14 +922,47 @@ int
agent_scd_setattr (const char *name,
const unsigned char *value, size_t valuelen)
{
- APP app;
+ app_t app;
int rc;
app = current_app? current_app : open_card ();
if (!app)
return gpg_error (GPG_ERR_CARD);
- rc = app->fnc.setattr (app, name, pin_cb, NULL, value, valuelen);
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+ char *p;
+
+ /* We assume that NAME does not need escaping. */
+ if (12 + strlen (name) > DIM(line)-1)
+ return gpg_error (GPG_ERR_CARD);
+ p = stpcpy (stpcpy (line, "SCD SETATTR "), name);
+ *p++ = ' ';
+ for (; valuelen; value++, valuelen--)
+ {
+ if (p >= line + DIM(line)-5 )
+ return gpg_error (GPG_ERR_CARD);
+ if (*value < ' ' || *value == '+' || *value == '%')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ *p = 0;
+
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETATTR");
+ }
+ else
+ {
+ rc = app->fnc.setattr (app, name, pin_cb, NULL, value, valuelen);
+ }
if (rc)
write_status (STATUS_SC_OP_FAILURE);
@@ -812,7 +970,7 @@ agent_scd_setattr (const char *name,
}
-static int
+static assuan_error_t
genkey_status_cb (void *opaque, const char *line)
{
struct agent_card_genkey_s *parm = opaque;
@@ -868,8 +1026,8 @@ genkey_status_cb (void *opaque, const char *line)
int
agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
{
- APP app;
- char keynostr[20];
+ app_t app;
+ char line[ASSUAN_LINELENGTH];
struct ctrl_ctx_s ctrl;
int rc;
@@ -878,18 +1036,44 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
return gpg_error (GPG_ERR_CARD);
memset (info, 0, sizeof *info);
- sprintf (keynostr, "%d", keyno);
- ctrl.status_cb = genkey_status_cb;
- ctrl.status_cb_arg = info;
- rc = app->fnc.genkey (app, &ctrl, keynostr,
- force? 1:0,
- pin_cb, NULL);
+ if (app->assuan_ctx)
+ {
+ snprintf (line, DIM(line)-1, "SCD GENKEY %s%d",
+ force? "--force ":"", keyno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL,
+ genkey_status_cb, info),
+ "SCD GENKEY");
+ }
+ else
+ {
+ snprintf (line, DIM(line)-1, "%d", keyno);
+ ctrl.status_cb = genkey_status_cb;
+ ctrl.status_cb_arg = info;
+ rc = app->fnc.genkey (app, &ctrl, line,
+ force? 1:0,
+ pin_cb, NULL);
+ }
+
if (rc)
write_status (STATUS_SC_OP_FAILURE);
return rc;
}
+
+static assuan_error_t
+membuf_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ membuf_t *data = opaque;
+
+ if (buffer)
+ put_membuf (data, buffer, length);
+ return 0;
+}
+
+
/* Send a PKSIGN command to the SCdaemon. */
int
agent_scd_pksign (const char *serialno, int hashalgo,
@@ -897,7 +1081,7 @@ agent_scd_pksign (const char *serialno, int hashalgo,
unsigned char **r_buf, size_t *r_buflen)
{
struct pincb_parm_s parm;
- APP app;
+ app_t app;
int rc;
*r_buf = NULL;
@@ -909,20 +1093,56 @@ agent_scd_pksign (const char *serialno, int hashalgo,
if (!app)
return gpg_error (GPG_ERR_CARD);
- /* Check that the card's serialnumber is as required.*/
- rc = check_card_serialno (app, serialno);
- if (rc == -1)
- goto retry;
+ if (app->assuan_ctx)
+ {
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+ int i;
+
+ if (indatalen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ p = stpcpy (line, "SCD SETDATA ");
+ for (i=0; i < indatalen ; i++, p += 2 )
+ sprintf (p, "%02X", indata[i]);
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETDATA");
+ if (!rc)
+ {
+ init_membuf (&data, 1024);
+ snprintf (line, DIM(line)-1, "SCD PKSIGN %s", serialno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ membuf_data_cb, &data,
+ NULL, NULL, NULL, NULL),
+ "SCD PKSIGN");
+ if (rc)
+ xfree (get_membuf (&data, &len));
+ else
+ *r_buf = get_membuf (&data, r_buflen);
+ }
+ }
+ else
+ {
+ /* Check that the card's serialnumber is as required.*/
+ rc = check_card_serialno (app, serialno);
+ if (rc == -1)
+ goto retry;
+
+ if (!rc)
+ rc = app->fnc.sign (app, serialno, hashalgo,
+ pin_cb, &parm,
+ indata, indatalen,
+ r_buf, r_buflen);
+ }
- if (!rc)
- rc = app->fnc.sign (app, serialno, hashalgo,
- pin_cb, &parm,
- indata, indatalen,
- r_buf, r_buflen);
if (rc)
{
write_status (STATUS_SC_OP_FAILURE);
- agent_clear_pin_cache (serialno);
+ if (!app->assuan_ctx)
+ agent_clear_pin_cache (serialno);
}
return rc;
}
@@ -935,7 +1155,7 @@ agent_scd_pkdecrypt (const char *serialno,
unsigned char **r_buf, size_t *r_buflen)
{
struct pincb_parm_s parm;
- APP app;
+ app_t app;
int rc;
*r_buf = NULL;
@@ -947,20 +1167,56 @@ agent_scd_pkdecrypt (const char *serialno,
if (!app)
return gpg_error (GPG_ERR_CARD);
- /* Check that the card's serialnumber is as required.*/
- rc = check_card_serialno (app, serialno);
- if (rc == -1)
- goto retry;
+ if (app->assuan_ctx)
+ {
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+ int i;
+
+ if (indatalen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ p = stpcpy (line, "SCD SETDATA ");
+ for (i=0; i < indatalen ; i++, p += 2 )
+ sprintf (p, "%02X", indata[i]);
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETDATA");
+ if (!rc)
+ {
+ init_membuf (&data, 1024);
+ snprintf (line, DIM(line)-1, "SCD PKDECRYPT %s", serialno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ membuf_data_cb, &data,
+ NULL, NULL, NULL, NULL),
+ "SCD PKDECRYPT");
+ if (rc)
+ xfree (get_membuf (&data, &len));
+ else
+ *r_buf = get_membuf (&data, r_buflen);
+ }
+ }
+ else
+ {
+ /* Check that the card's serialnumber is as required.*/
+ rc = check_card_serialno (app, serialno);
+ if (rc == -1)
+ goto retry;
+
+ if (!rc)
+ rc = app->fnc.decipher (app, serialno,
+ pin_cb, &parm,
+ indata, indatalen,
+ r_buf, r_buflen);
+ }
- if (!rc)
- rc = app->fnc.decipher (app, serialno,
- pin_cb, &parm,
- indata, indatalen,
- r_buf, r_buflen);
if (rc)
{
write_status (STATUS_SC_OP_FAILURE);
- agent_clear_pin_cache (serialno);
+ if (!app->assuan_ctx)
+ agent_clear_pin_cache (serialno);
}
return rc;
}
@@ -969,7 +1225,7 @@ agent_scd_pkdecrypt (const char *serialno,
int
agent_scd_change_pin (int chvno)
{
- APP app;
+ app_t app;
char chvnostr[20];
int reset = 0;
int rc;
@@ -981,9 +1237,17 @@ agent_scd_change_pin (int chvno)
if (!app)
return gpg_error (GPG_ERR_CARD);
- sprintf (chvnostr, "%d", chvno);
- rc = app->fnc.change_pin (app, NULL, chvnostr, reset,
- pin_cb, NULL);
+ if (app->assuan_ctx)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ sprintf (chvnostr, "%d", chvno);
+ rc = app->fnc.change_pin (app, NULL, chvnostr, reset,
+ pin_cb, NULL);
+ }
+
if (rc)
write_status (STATUS_SC_OP_FAILURE);
return rc;
@@ -995,14 +1259,22 @@ agent_scd_change_pin (int chvno)
int
agent_scd_checkpin (const char *serialnobuf)
{
- APP app;
+ app_t app;
int rc;
app = current_app? current_app : open_card ();
if (!app)
return gpg_error (GPG_ERR_CARD);
- rc = app->fnc.check_pin (app, serialnobuf, pin_cb, NULL);
+ if (app->assuan_ctx)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ rc = app->fnc.check_pin (app, serialnobuf, pin_cb, NULL);
+ }
+
if (rc)
write_status (STATUS_SC_OP_FAILURE);
return rc;
@@ -1017,16 +1289,24 @@ agent_openpgp_storekey (int keyno,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen)
{
- APP app;
+ app_t app;
int rc;
app = current_app? current_app : open_card ();
if (!app)
return gpg_error (GPG_ERR_CARD);
- rc = app_openpgp_storekey (app, keyno, template, template_len,
- created_at, m, mlen, e, elen,
- pin_cb, NULL);
+ if (app->assuan_ctx)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ rc = app_openpgp_storekey (app, keyno, template, template_len,
+ created_at, m, mlen, e, elen,
+ pin_cb, NULL);
+ }
+
if (rc)
write_status (STATUS_SC_OP_FAILURE);
return rc;