aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am7
-rw-r--r--agent/Makefile.am2
-rw-r--r--agent/agent.h13
-rw-r--r--agent/call-tkd.c370
-rw-r--r--agent/command.c80
-rw-r--r--agent/divert-tkd.c45
-rw-r--r--agent/findkey.c66
-rw-r--r--agent/gpg-agent.c6
-rw-r--r--agent/keyformat.txt12
-rw-r--r--agent/pksign.c4
-rw-r--r--agent/protect.c22
-rw-r--r--am/cmacros.am3
-rw-r--r--common/mapstrings.c1
-rw-r--r--common/util.h1
-rw-r--r--configure.ac21
-rw-r--r--tkd/Makefile.am37
-rw-r--r--tkd/command.c682
-rw-r--r--tkd/pkcs11.c1407
-rw-r--r--tkd/pkcs11.h1384
-rw-r--r--tkd/tkdaemon.c1293
-rw-r--r--tkd/tkdaemon.h126
21 files changed, 5547 insertions, 35 deletions
diff --git a/Makefile.am b/Makefile.am
index 6a2508b11..6ee94561a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -87,6 +87,11 @@ scd = scd
else
scd =
endif
+if BUILD_TKDAEMON
+tkd = tkd
+else
+tkd =
+endif
if BUILD_G13
g13 = g13
else
@@ -114,7 +119,7 @@ tpm2d =
endif
SUBDIRS = m4 common regexp kbx \
- ${gpg} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \
+ ${gpg} ${sm} ${agent} ${scd} ${tkd} ${g13} ${dirmngr} \
tools po ${doc} ${tests} ${tpm2d}
dist_doc_DATA = README
diff --git a/agent/Makefile.am b/agent/Makefile.am
index 4da1ea9d8..587aa7ae8 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -56,8 +56,10 @@ gpg_agent_SOURCES = \
protect.c \
trustlist.c \
divert-scd.c \
+ divert-tkd.c \
cvt-openpgp.c cvt-openpgp.h \
call-scd.c \
+ call-tkd.c \
call-daemon.c \
$(tpm2_sources) \
learncard.c
diff --git a/agent/agent.h b/agent/agent.h
index 4e7452eee..54076223d 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -60,6 +60,7 @@
enum daemon_type
{
DAEMON_SCD,
+ DAEMON_TKD,
DAEMON_TPM2D,
DAEMON_MAX_TYPE
};
@@ -660,6 +661,11 @@ int divert_generic_cmd (ctrl_t ctrl,
gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno,
const char *keyref,
const char *keydata, size_t keydatalen);
+/*-- divert-tkd.c --*/
+int divert_tkd_pksign (ctrl_t ctrl,
+ const unsigned char *digest, size_t digestlen,
+ unsigned char **r_sig, size_t *r_siglen);
+int divert_tkd_cmd (ctrl_t ctrl, const char *cmdline);
/*-- call-daemon.c --*/
gpg_error_t daemon_start (enum daemon_type type, ctrl_t ctrl);
@@ -730,6 +736,13 @@ void agent_card_free_keyinfo (struct card_key_info_s *l);
gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip,
int cap, struct card_key_info_s **result);
+/*-- call-tkd.c --*/
+int agent_tkd_pksign (ctrl_t ctrl,
+ const unsigned char *indata, size_t indatalen,
+ unsigned char **r_buf, size_t *r_buflen);
+int agent_tkd_readkey (ctrl_t ctrl, const char *keygrip,
+ unsigned char **r_buf, size_t *r_buflen);
+int agent_tkd_cmd (ctrl_t ctrl, const char *cmdline);
/*-- learncard.c --*/
int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force);
diff --git a/agent/call-tkd.c b/agent/call-tkd.c
new file mode 100644
index 000000000..e0c85e9eb
--- /dev/null
+++ b/agent/call-tkd.c
@@ -0,0 +1,370 @@
+/* call-tkd.c - fork of the tkdaemon to do TK operations
+ * Copyright (C) 2001, 2002, 2005, 2007, 2010,
+ * 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include "agent.h"
+#include <assuan.h>
+#include "../common/strlist.h"
+#include "../common/sexp-parse.h"
+#include "../common/i18n.h"
+
+static int
+start_tkd (ctrl_t ctrl)
+{
+ return daemon_start (DAEMON_TKD, ctrl);
+}
+
+static int
+unlock_tkd (ctrl_t ctrl, gpg_error_t err)
+{
+ return daemon_unlock (DAEMON_TKD, ctrl, err);
+}
+
+static assuan_context_t
+daemon_ctx (ctrl_t ctrl)
+{
+ return daemon_type_ctx (DAEMON_TKD, ctrl);
+}
+
+struct inq_parm_s {
+ assuan_context_t ctx;
+ gpg_error_t (*getpin_cb)(ctrl_t, const char *, char **);
+ ctrl_t ctrl;
+ /* The next fields are used by inq_keydata. */
+ const unsigned char *keydata;
+ size_t keydatalen;
+ /* following only used by inq_extra */
+ const unsigned char *extra;
+ size_t extralen;
+ char *pin;
+};
+
+static gpg_error_t
+inq_needpin (void *opaque, const char *line)
+{
+ struct inq_parm_s *parm = opaque;
+ char *pin = NULL;
+ gpg_error_t rc;
+ const char *s;
+
+ if ((s = has_leading_keyword (line, "NEEDPIN")))
+ {
+ rc = parm->getpin_cb (parm->ctrl, s, &pin);
+ if (!rc)
+ rc = assuan_send_data (parm->ctx, pin, strlen(pin));
+ parm->pin = pin;
+ }
+ else
+ {
+ log_error ("unsupported inquiry '%s'\n", line);
+ rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+ }
+
+ return rc;
+}
+
+static gpg_error_t
+inq_extra (void *opaque, const char *line)
+{
+ struct inq_parm_s *parm = opaque;
+
+ if (has_leading_keyword (line, "EXTRA"))
+ return assuan_send_data (parm->ctx, parm->extra, parm->extralen);
+ else
+ return inq_needpin (opaque, line);
+}
+
+static gpg_error_t
+pin_cb (ctrl_t ctrl, const char *prompt, char **passphrase)
+{
+ char hexgrip[2*KEYGRIP_LEN + 1];
+
+ bin2hex (ctrl->keygrip, KEYGRIP_LEN, hexgrip);
+ *passphrase = agent_get_cache (ctrl, hexgrip, CACHE_MODE_USER);
+ if (*passphrase)
+ return 0;
+ return agent_get_passphrase (ctrl, passphrase,
+ _("Please enter your passphrase, so that the "
+ "secret key can be unlocked for this session"),
+ prompt, NULL, 0,
+ hexgrip, CACHE_MODE_USER, NULL);
+}
+
+/* Read a key with KEYGRIP and return it in a malloced buffer pointed
+ * to by R_BUF as a valid S-expression. If R_BUFLEN is not NULL the
+ * length is stored there. */
+int
+agent_tkd_readkey (ctrl_t ctrl, const char *keygrip,
+ unsigned char **r_buf, size_t *r_buflen)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t buflen;
+ struct inq_parm_s inqparm;
+
+ *r_buf = NULL;
+ if (r_buflen)
+ *r_buflen = 0;
+
+ rc = start_tkd (ctrl);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 1024);
+
+ inqparm.ctx = daemon_ctx (ctrl);
+ inqparm.getpin_cb = pin_cb;
+ inqparm.ctrl = ctrl;
+ inqparm.pin = NULL;
+
+ snprintf (line, DIM(line), "READKEY %s", keygrip);
+ rc = assuan_transact (daemon_ctx (ctrl), line,
+ put_membuf_cb, &data,
+ inq_needpin, &inqparm,
+ NULL, NULL);
+ if (rc)
+ {
+ xfree (get_membuf (&data, &buflen));
+ return unlock_tkd (ctrl, rc);
+ }
+ *r_buf = get_membuf (&data, &buflen);
+ if (!*r_buf)
+ return unlock_tkd (ctrl, gpg_error (GPG_ERR_ENOMEM));
+
+ if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL))
+ {
+ xfree (*r_buf); *r_buf = NULL;
+ return unlock_tkd (ctrl, gpg_error (GPG_ERR_INV_VALUE));
+ }
+ if (r_buflen)
+ *r_buflen = buflen;
+
+ return unlock_tkd (ctrl, 0);
+}
+
+
+/* Helper returning a command option to describe the used hash
+ algorithm. See scd/command.c:cmd_pksign. */
+static const char *
+hash_algo_option (int algo)
+{
+ switch (algo)
+ {
+ case GCRY_MD_MD5 : return "--hash=md5";
+ case GCRY_MD_RMD160: return "--hash=rmd160";
+ case GCRY_MD_SHA1 : return "--hash=sha1";
+ case GCRY_MD_SHA224: return "--hash=sha224";
+ case GCRY_MD_SHA256: return "--hash=sha256";
+ case GCRY_MD_SHA384: return "--hash=sha384";
+ case GCRY_MD_SHA512: return "--hash=sha512";
+ default: return "";
+ }
+}
+
+
+int
+agent_tkd_pksign (ctrl_t ctrl, const unsigned char *digest, size_t digestlen,
+ unsigned char **r_sig, size_t *r_siglen)
+{
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ struct inq_parm_s inqparm;
+ char hexgrip[2*KEYGRIP_LEN + 1];
+
+ rc = start_tkd (ctrl);
+ if (rc)
+ return rc;
+
+ init_membuf (&data, 1024);
+
+ inqparm.ctx = daemon_ctx (ctrl);
+ inqparm.getpin_cb = pin_cb;
+ inqparm.pin = NULL;
+ inqparm.ctrl = ctrl;
+ inqparm.extra = digest;
+ inqparm.extralen = digestlen;
+
+ bin2hex (ctrl->keygrip, KEYGRIP_LEN, hexgrip);
+ snprintf (line, sizeof(line), "PKSIGN %s %s",
+ hash_algo_option (ctrl->digest.algo), hexgrip);
+
+ rc = assuan_transact (daemon_ctx (ctrl), line,
+ put_membuf_cb, &data,
+ inq_extra, &inqparm,
+ NULL, NULL);
+ if (!rc)
+ {
+ bin2hex (ctrl->keygrip, KEYGRIP_LEN, hexgrip);
+ agent_put_cache (ctrl, hexgrip, CACHE_MODE_USER, inqparm.pin, 0);
+ }
+
+ xfree (inqparm.pin);
+
+ if (rc)
+ {
+ size_t len;
+ xfree (get_membuf (&data, &len));
+ return unlock_tkd (ctrl, rc);
+ }
+
+ *r_sig = get_membuf (&data, r_siglen);
+
+ return unlock_tkd (ctrl, 0);
+}
+
+/* This handler is a helper for pincache_put_cb but may also be called
+ * directly for that status code with ARGS being the arguments after
+ * the status keyword (and with white space removed). */
+static gpg_error_t
+handle_pincache_put (const char *args)
+{
+ gpg_error_t err;
+ const char *s, *key, *pin;
+ char *keybuf = NULL;
+ size_t keylen;
+
+ key = s = args;
+ while (*s && !spacep (s))
+ s++;
+ keylen = s - key;
+ if (keylen < 3)
+ {
+ /* At least we need 2 slashes and slot number. */
+ log_error ("%s: ignoring invalid key\n", __func__);
+ err = 0;
+ goto leave;
+ }
+
+ keybuf = xtrymalloc (keylen+1);
+ if (!keybuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (keybuf, key, keylen);
+ keybuf[keylen] = 0;
+ key = keybuf;
+
+ while (spacep (s))
+ s++;
+ pin = s;
+ if (!*pin)
+ {
+ /* No value - flush the cache. The cache module knows aboput
+ * the structure of the key to flush only parts. */
+ log_debug ("%s: flushing cache '%s'\n", __func__, key);
+ agent_put_cache (NULL, key, CACHE_MODE_PIN, NULL, -1);
+ err = 0;
+ goto leave;
+ }
+
+ log_debug ("%s: caching '%s'->'%s'\n", __func__, key, pin);
+ agent_put_cache (NULL, key, CACHE_MODE_PIN, pin, -1);
+ err = 0;
+
+ leave:
+ xfree (keybuf);
+ return err;
+}
+
+static gpg_error_t
+pass_status_thru (void *opaque, const char *line)
+{
+ gpg_error_t err = 0;
+ assuan_context_t ctx = opaque;
+ char keyword[200];
+ int i;
+
+ if (line[0] == '#' && (!line[1] || spacep (line+1)))
+ {
+ /* We are called in convey comments mode. Now, if we see a
+ comment marker as keyword we forward the line verbatim to the
+ the caller. This way the comment lines from scdaemon won't
+ appear as status lines with keyword '#'. */
+ assuan_write_line (ctx, line);
+ }
+ else
+ {
+ for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++)
+ keyword[i] = *line;
+ keyword[i] = 0;
+
+ /* Truncate any remaining keyword stuff. */
+ for (; *line && !spacep (line); line++)
+ ;
+ while (spacep (line))
+ line++;
+
+ /* We do not want to pass PINCACHE_PUT through. */
+ if (!strcmp (keyword, "PINCACHE_PUT"))
+ err = handle_pincache_put (line);
+ else
+ assuan_write_status (ctx, keyword, line);
+ }
+ return err;
+}
+
+static gpg_error_t
+pass_data_thru (void *opaque, const void *buffer, size_t length)
+{
+ assuan_context_t ctx = opaque;
+
+ assuan_send_data (ctx, buffer, length);
+ return 0;
+}
+
+int
+agent_tkd_cmd (ctrl_t ctrl, const char *cmdline)
+{
+ int rc;
+ struct inq_parm_s inqparm;
+ int saveflag;
+
+ rc = start_tkd (ctrl);
+ if (rc)
+ return rc;
+
+ inqparm.ctx = daemon_ctx (ctrl);
+ inqparm.getpin_cb = pin_cb;
+ inqparm.pin = NULL;
+
+ saveflag = assuan_get_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS);
+ assuan_set_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS, 1);
+ rc = assuan_transact (daemon_ctx (ctrl), cmdline,
+ pass_data_thru, daemon_ctx (ctrl),
+ inq_needpin, &inqparm,
+ pass_status_thru, daemon_ctx (ctrl));
+
+ assuan_set_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS, saveflag);
+
+ return unlock_tkd (ctrl, rc);
+}
diff --git a/agent/command.c b/agent/command.c
index 2e996d096..9351ef615 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1297,6 +1297,7 @@ cmd_keyattr (assuan_context_t ctx, char *line)
static const char hlp_readkey[] =
"READKEY [--no-data] [--format=ssh] <hexstring_with_keygrip>\n"
" --card <keyid>\n"
+ " --token <hexstring_with_keygrip>\n"
"\n"
"Return the public key for the given keygrip or keyid.\n"
"With --card, private key file with card information will be created.";
@@ -1309,13 +1310,14 @@ cmd_readkey (assuan_context_t ctx, char *line)
gcry_sexp_t s_pkey = NULL;
unsigned char *pkbuf = NULL;
size_t pkbuflen;
- int opt_card, opt_no_data, opt_format_ssh;
+ int opt_card, opt_token, opt_no_data, opt_format_ssh;
if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
opt_no_data = has_option (line, "--no-data");
opt_card = has_option (line, "--card");
+ opt_token = has_option (line, "--token");
opt_format_ssh = has_option (line, "--format=ssh");
line = skip_options (line);
@@ -1367,6 +1369,34 @@ cmd_readkey (assuan_context_t ctx, char *line)
xfree (serialno);
xfree (keyidbuf);
}
+ else if (opt_token)
+ {
+ const char *keygrip = line;
+
+ rc = agent_tkd_readkey (ctrl, keygrip, &pkbuf, &pkbuflen);
+ if (rc)
+ goto leave;
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)pkbuf, pkbuflen);
+ if (rc)
+ goto leave;
+
+ if (!gcry_pk_get_keygrip (s_pkey, grip))
+ {
+ rc = gcry_pk_testkey (s_pkey);
+ if (rc == 0)
+ rc = gpg_error (GPG_ERR_INTERNAL);
+
+ goto leave;
+ }
+
+ if (agent_key_available (grip))
+ {
+ /* (Shadow)-key is not available in our key storage. */
+ rc = agent_write_shadow_key (grip, NULL, NULL, pkbuf, 0);
+ if (rc)
+ goto leave;
+ }
+ }
else
{
rc = parse_keygrip (ctx, line, grip);
@@ -2648,6 +2678,53 @@ cmd_scd (assuan_context_t ctx, char *line)
}
+static const char hlp_tkd[] =
+ "TKD <commands to pass to the tkdaemon>\n"
+ " \n"
+ "This is a general quote command to redirect everything to the\n"
+ "TKdaemon.";
+static gpg_error_t
+cmd_tkd (assuan_context_t ctx, char *line)
+{
+ int rc;
+#ifdef BUILD_WITH_TKDAEMON
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ if (ctrl->restricted)
+ {
+ const char *argv[5];
+ int argc;
+ char *l;
+
+ l = xtrystrdup (line);
+ if (!l)
+ return gpg_error_from_syserror ();
+
+ argc = split_fields (l, argv, DIM (argv));
+
+ /* These commands are allowed. */
+ if ((argc >= 1 && !strcmp (argv[0], "SLOTLIST"))
+ || (argc == 2
+ && !strcmp (argv[0], "GETINFO")
+ && !strcmp (argv[1], "version"))
+ || (argc == 2
+ && !strcmp (argv[0], "KEYINFO")
+ && !strcmp (argv[1], "--list=encr")))
+ xfree (l);
+ else
+ {
+ xfree (l);
+ return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+ }
+ }
+
+ rc = divert_tkd_cmd (ctrl, line);
+#else
+ (void)ctx; (void)line;
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+#endif
+ return rc;
+}
static const char hlp_keywrap_key[] =
"KEYWRAP_KEY [--clear] <mode>\n"
@@ -4198,6 +4275,7 @@ register_commands (assuan_context_t ctx)
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "SCD", cmd_scd, hlp_scd },
+ { "TKD", cmd_tkd, hlp_tkd },
{ "KEYWRAP_KEY", cmd_keywrap_key, hlp_keywrap_key },
{ "IMPORT_KEY", cmd_import_key, hlp_import_key },
{ "EXPORT_KEY", cmd_export_key, hlp_export_key },
diff --git a/agent/divert-tkd.c b/agent/divert-tkd.c
new file mode 100644
index 000000000..3fd1872f1
--- /dev/null
+++ b/agent/divert-tkd.c
@@ -0,0 +1,45 @@
+/* divert-tkd.c - divert operations to the tkdaemon
+ * Copyright (C) 2002, 2003, 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "agent.h"
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+int
+divert_tkd_pksign (ctrl_t ctrl, const unsigned char *digest, size_t digestlen,
+ unsigned char **r_sig, size_t *r_siglen)
+{
+ return agent_tkd_pksign (ctrl, digest, digestlen, r_sig, r_siglen);
+}
+
+int
+divert_tkd_cmd (ctrl_t ctrl, const char *cmdline)
+{
+ return agent_tkd_cmd (ctrl, cmdline);
+}
diff --git a/agent/findkey.c b/agent/findkey.c
index 098d5224f..3544764d0 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1309,24 +1309,36 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
err = agent_get_shadow_info_type (buf, &s, &shadow_type);
if (!err)
{
- n = gcry_sexp_canon_len (s, 0, NULL,NULL);
- log_assert (n);
- *shadow_info = xtrymalloc (n);
- if (!*shadow_info)
+ if (!s)
{
- err = out_of_core ();
- goto shadow_error;
+ *shadow_info = xstrdup ("tkd");
+ if (!*shadow_info)
+ {
+ err = out_of_core ();
+ goto shadow_error;
+ }
}
else
{
- memcpy (*shadow_info, s, n);
- /*
- * When it's a key on card (not on tpm2), maks sure
- * it's available.
- */
- if (strcmp (shadow_type, "t1-v1") == 0 && !grip)
- err = prompt_for_card (ctrl, ctrl->keygrip,
- keymeta, *shadow_info);
+ n = gcry_sexp_canon_len (s, 0, NULL,NULL);
+ log_assert (n);
+ *shadow_info = xtrymalloc (n);
+ if (!*shadow_info)
+ {
+ err = out_of_core ();
+ goto shadow_error;
+ }
+ else
+ {
+ memcpy (*shadow_info, s, n);
+ /*
+ * When it's a key on card (not on tpm2), make sure
+ * it's available.
+ */
+ if (strcmp (shadow_type, "t1-v1") == 0 && !grip)
+ err = prompt_for_card (ctrl, ctrl->keygrip,
+ keymeta, *shadow_info);
+ }
}
}
else
@@ -1801,16 +1813,22 @@ agent_write_shadow_key (const unsigned char *grip,
unsigned char *shdkey;
size_t len;
- /* Just in case some caller did not parse the stuff correctly, skip
- * leading spaces. */
- while (spacep (serialno))
- serialno++;
- while (spacep (keyid))
- keyid++;
-
- shadow_info = make_shadow_info (serialno, keyid);
- if (!shadow_info)
- return gpg_error_from_syserror ();
+ if (serialno == NULL && keyid == NULL)
+ /* It's a token, identified by the keygrip. */
+ shadow_info = NULL;
+ else
+ {
+ /* Just in case some caller did not parse the stuff correctly, skip
+ * leading spaces. */
+ while (spacep (serialno))
+ serialno++;
+ while (spacep (keyid))
+ keyid++;
+
+ shadow_info = make_shadow_info (serialno, keyid);
+ if (!shadow_info)
+ return gpg_error_from_syserror ();
+ }
err = agent_shadow_key (pkbuf, shadow_info, &shdkey);
xfree (shadow_info);
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 1db422737..11815b4b5 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -104,6 +104,7 @@ enum cmd_and_opt_values
oLCmessages,
oXauthority,
oScdaemonProgram,
+ oTkdaemonProgram,
oTpm2daemonProgram,
oDefCacheTTL,
oDefCacheTTLSSH,
@@ -207,6 +208,8 @@ static gpgrt_opt_t opts[] = {
/* */ N_("do not use the SCdaemon") ),
ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
/* */ N_("|PGM|use PGM as the SCdaemon program") ),
+ ARGPARSE_s_s (oTkdaemonProgram, "tkdaemon-program",
+ /* */ N_("|PGM|use PGM as the TKdaemon program") ),
ARGPARSE_s_s (oTpm2daemonProgram, "tpm2daemon-program",
/* */ N_("|PGM|use PGM as the tpm2daemon program") ),
ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
@@ -942,6 +945,9 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
case oScdaemonProgram:
opt.daemon_program[DAEMON_SCD] = pargs->r.ret_str;
break;
+ case oTkdaemonProgram:
+ opt.daemon_program[DAEMON_TKD] = pargs->r.ret_str;
+ break;
case oDisableScdaemon: opt.disable_daemon[DAEMON_SCD] = 1; break;
case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index bbcaa7e2c..b8b814f88 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -342,9 +342,11 @@ to keys stored on a token:
(comment whatever)
)
-The currently used protocols are "t1-v1" (token info version 1) and
-"tpm2-v1" (TPM format key information). The second list with the
-information has this layout for "t1-v1":
+The currently used protocols are "t1-v1" (token info version 1),
+"tpm2-v1" (TPM format key information), and "tkd-v1" (token daemon
+info version 1).
+
+The second list with the information has this layout for "t1-v1":
(card_serial_number id_string_of_key fixed_pin_length)
@@ -353,13 +355,15 @@ the PIN; a value of 0 indicates that this information is not
available. The rationale for this field is that some pinpad equipped
readers don't allow passing a variable length PIN.
-This is the (info) layout for "tpm2-v1":
+For "tpm2-v1", the layout of the (info) is:
(parent tpm_private_string tpm_public_string)
Although this precise format is encapsulated inside the tpm2daemon
itself and nothing in gpg ever uses this.
+For "tkd-v1", the layout of the (info) part is nothing.
+
More items may be added to the list.
** OpenPGP Private Key Transfer Format
diff --git a/agent/pksign.c b/agent/pksign.c
index dfed0e398..1ee278aac 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -392,7 +392,9 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
}
{
- if (agent_is_tpm2_key (s_skey))
+ if (!strcmp (shadow_info, "tkd"))
+ err = divert_tkd_pksign (ctrl, data, datalen, &buf, &len);
+ else if (agent_is_tpm2_key (s_skey))
err = divert_tpm2_pksign (ctrl,
data, datalen,
ctrl->digest.algo,
diff --git a/agent/protect.c b/agent/protect.c
index 7197cf7e6..a40be0f9a 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1477,10 +1477,15 @@ agent_shadow_key_type (const unsigned char *pubkey,
int depth = 0;
char *p;
size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
- size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
+ size_t shadow_info_len;
- if (!pubkey_len || !shadow_info_len)
+ if (!pubkey_len)
return gpg_error (GPG_ERR_INV_VALUE);
+ if (shadow_info)
+ shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
+ else
+ shadow_info_len = 0;
+
s = pubkey;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
@@ -1536,7 +1541,8 @@ agent_shadow_key_type (const unsigned char *pubkey,
memcpy (p, pubkey+14, point - (pubkey+14));
p += point - (pubkey+14);
p += sprintf (p, "(8:shadowed%d:%s", (int)strlen(type), type);
- memcpy (p, shadow_info, shadow_info_len);
+ if (shadow_info_len)
+ memcpy (p, shadow_info, shadow_info_len);
p += shadow_info_len;
*p++ = ')';
memcpy (p, point, pubkey_len - (point - pubkey));
@@ -1550,7 +1556,10 @@ agent_shadow_key (const unsigned char *pubkey,
const unsigned char *shadow_info,
unsigned char **result)
{
- return agent_shadow_key_type (pubkey, shadow_info, "t1-v1", result);
+ if (shadow_info)
+ return agent_shadow_key_type (pubkey, shadow_info, "t1-v1", result);
+ else
+ return agent_shadow_key_type (pubkey, NULL, "tkd-v1", result);
}
/* Parse a canonical encoded shadowed key and return a pointer to the
@@ -1620,6 +1629,11 @@ agent_get_shadow_info_type (const unsigned char *shadowkey,
if (shadow_info)
*shadow_info = s;
}
+ else if (smatch(&s, n, "tkd-v1"))
+ {
+ if (shadow_info)
+ *shadow_info = NULL;
+ }
else
return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
s = saved_s;
diff --git a/am/cmacros.am b/am/cmacros.am
index d6d466fb6..0841efd86 100644
--- a/am/cmacros.am
+++ b/am/cmacros.am
@@ -44,6 +44,9 @@ endif
if GNUPG_SCDAEMON_PGM
AM_CPPFLAGS += -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
endif
+if GNUPG_TKDAEMON_PGM
+AM_CPPFLAGS += -DGNUPG_DEFAULT_TKDAEMON="\"@GNUPG_TKDAEMON_PGM@\""
+endif
if GNUPG_TPM2DAEMON_PGM
AM_CPPFLAGS += -DGNUPG_DEFAULT_TPM2DAEMON="\"@GNUPG_TPM2DAEMON_PGM@\""
endif
diff --git a/common/mapstrings.c b/common/mapstrings.c
index 9efe95c0d..f95dd8ad9 100644
--- a/common/mapstrings.c
+++ b/common/mapstrings.c
@@ -50,6 +50,7 @@ static struct {
{ "GPGSM", GPGSM_NAME },
{ "GPG_AGENT", GPG_AGENT_NAME },
{ "SCDAEMON", SCDAEMON_NAME },
+ { "TKDAEMON", TKDAEMON_NAME },
{ "TPM2DAEMON",TPM2DAEMON_NAME},
{ "DIRMNGR", DIRMNGR_NAME },
{ "G13", G13_NAME },
diff --git a/common/util.h b/common/util.h
index d80e4fb25..899b775ea 100644
--- a/common/util.h
+++ b/common/util.h
@@ -298,6 +298,7 @@ char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info);
#define GNUPG_MODULE_NAME_KEYBOXD 13
#define GNUPG_MODULE_NAME_TPM2DAEMON 14
#define GNUPG_MODULE_NAME_CARD 15
+#define GNUPG_MODULE_NAME_TKDAEMON 16
const char *gnupg_module_name (int which);
void gnupg_module_name_flush_some (void);
void gnupg_set_builddir (const char *newdir);
diff --git a/configure.ac b/configure.ac
index c31ae026a..cc12667ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -125,6 +125,7 @@ GNUPG_BUILD_PROGRAM(gpgsm, yes)
# The agent is a required part and can't be disabled anymore.
build_agent=yes
GNUPG_BUILD_PROGRAM(scdaemon, yes)
+GNUPG_BUILD_PROGRAM(tkdaemon, yes)
GNUPG_BUILD_PROGRAM(g13, no)
GNUPG_BUILD_PROGRAM(dirmngr, yes)
GNUPG_BUILD_PROGRAM(keyboxd, yes)
@@ -186,6 +187,13 @@ AM_CONDITIONAL(GNUPG_SCDAEMON_PGM, test -n "$GNUPG_SCDAEMON_PGM")
show_gnupg_scdaemon_pgm="(default)"
test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
+AC_ARG_WITH(tkdaemon-pgm,
+ [ --with-tkdaemon-pgm=PATH Use PATH as the default for the tkdaemon)],
+ GNUPG_TKDAEMON_PGM="$withval", GNUPG_TKDAEMON_PGM="" )
+AC_SUBST(GNUPG_TKDAEMON_PGM)
+AM_CONDITIONAL(GNUPG_TKDAEMON_PGM, test -n "$GNUPG_TKDAEMON_PGM")
+show_gnupg_tkdaemon_pgm="(default)"
+test -n "$GNUPG_TKDAEMON_PGM" && show_gnupg_tkdaemon_pgm="$GNUPG_TKDAEMON_PGM"
AC_ARG_WITH(tpm2daemon-pgm,
[ --with-tpm2daemon-pgm=PATH Use PATH as the default for the tpm2daemon)],
@@ -1840,6 +1848,7 @@ AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "yes")
AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes")
AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes")
AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes")
+AM_CONDITIONAL(BUILD_TKDAEMON, test "$build_tkdaemon" = "yes")
AM_CONDITIONAL(BUILD_G13, test "$build_g13" = "yes")
AM_CONDITIONAL(BUILD_DIRMNGR, test "$build_dirmngr" = "yes")
AM_CONDITIONAL(BUILD_KEYBOXD, test "$build_keyboxd" = "yes")
@@ -1868,6 +1877,9 @@ fi
if test "$build_scdaemon" = yes ; then
AC_DEFINE(BUILD_WITH_SCDAEMON,1,[Defined if SCDAEMON is to be build])
fi
+if test "$build_tkdaemon" = yes ; then
+ AC_DEFINE(BUILD_WITH_TKDAEMON,1,[Defined if TKDAEMON is to be build])
+fi
if test "$build_dirmngr" = yes ; then
AC_DEFINE(BUILD_WITH_DIRMNGR,1,[Defined if DIRMNGR is to be build])
fi
@@ -1905,6 +1917,10 @@ AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon])
AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon",
[The displayed name of scdaemon])
+AC_DEFINE_UNQUOTED(TKDAEMON_NAME, "tkdaemon", [The name of the tkdaemon])
+AC_DEFINE_UNQUOTED(TKDAEMON_DISP_NAME, "TKDaemon",
+ [The displayed name of tkdaemon])
+
AC_DEFINE_UNQUOTED(DIRMNGR_NAME, "dirmngr", [The name of the dirmngr])
AC_DEFINE_UNQUOTED(DIRMNGR_DISP_NAME, "DirMngr",
[The displayed name of dirmngr])
@@ -1934,6 +1950,8 @@ AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO",
[The name of the dirmngr info envvar])
AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
[The name of the SCdaemon socket])
+AC_DEFINE_UNQUOTED(TKDAEMON_SOCK_NAME, "S.tkdaemon",
+ [The name of the TKDaemon socket])
AC_DEFINE_UNQUOTED(KEYBOXD_SOCK_NAME, "S.keyboxd",
[The name of the keyboxd socket])
AC_DEFINE_UNQUOTED(TPM2DAEMON_SOCK_NAME, "S.tpm2daemon",
@@ -2088,6 +2106,7 @@ g10/Makefile
sm/Makefile
agent/Makefile
scd/Makefile
+tkd/Makefile
tpm2d/Makefile
g13/Makefile
dirmngr/Makefile
@@ -2128,6 +2147,7 @@ echo "
S/MIME: $build_gpgsm
Agent: $build_agent
Smartcard: $build_scdaemon $build_scdaemon_extra
+ token: $build_tkdaemon $build_tkdaemon_extra
TPM: $build_tpm2d $show_tss_type
G13: $build_g13
Dirmngr: $build_dirmngr
@@ -2140,6 +2160,7 @@ echo "
Default agent: $show_gnupg_agent_pgm
Default pinentry: $show_gnupg_pinentry_pgm
Default scdaemon: $show_gnupg_scdaemon_pgm
+ Default tkdaemon: $show_gnupg_tkdaemon_pgm
Default keyboxd: $show_gnupg_keyboxd_pgm
Default tpm2daemon: $show_gnupg_tpm2daemon_pgm
Default dirmngr: $show_gnupg_dirmngr_pgm
diff --git a/tkd/Makefile.am b/tkd/Makefile.am
new file mode 100644
index 000000000..a86e13f04
--- /dev/null
+++ b/tkd/Makefile.am
@@ -0,0 +1,37 @@
+# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+EXTRA_DIST =
+
+libexec_PROGRAMS = tkdaemon
+
+AM_CPPFLAGS =
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+
+include $(top_srcdir)/am/cmacros.am
+
+tkdaemon_SOURCES = \
+ tkdaemon.c tkdaemon.h \
+ command.c pkcs11.c
+
+tkdaemon_LDADD = $(libcommonpth) \
+ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(GPG_ERROR_LIBS) \
+ $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV)
diff --git a/tkd/command.c b/tkd/command.c
new file mode 100644
index 000000000..a78ede3b7
--- /dev/null
+++ b/tkd/command.c
@@ -0,0 +1,682 @@
+/* command.c - TKdaemon command handler
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef USE_NPTH
+# include <npth.h>
+#endif
+
+#include "tkdaemon.h"
+#include "../common/asshelp.h"
+#include "../common/server-help.h"
+#include "../common/ssh-utils.h"
+
+/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
+ * length needs to small compared to the maximum Assuan line length. */
+#define MAXLEN_PIN 100
+
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+
+/* Data used to associate an Assuan context with local server data.
+ This object describes the local properties of one session. */
+struct server_local_s
+{
+ /* We keep a list of all active sessions with the anchor at
+ SESSION_LIST (see below). This field is used for linking. */
+ struct server_local_s *next_session;
+
+ /* This object is usually assigned to a CTRL object (which is
+ globally visible). While enumerating all sessions we sometimes
+ need to access data of the CTRL object; thus we keep a
+ backpointer here. */
+ ctrl_t ctrl_backlink;
+
+ /* The Assuan context used by this session/server. */
+ assuan_context_t assuan_ctx;
+
+#ifdef HAVE_W32_SYSTEM
+ void *event_signal; /* Or NULL if not used. */
+#else
+ int event_signal; /* Or 0 if not used. */
+#endif
+
+ /* If set to true we will be terminate ourself at the end of the
+ this session. */
+ unsigned int stopme:1;
+};
+
+
+struct token_ctx_s
+{
+};
+
+/* To keep track of all running sessions, we link all active server
+ contexts and the anchor in this variable. */
+static struct server_local_s *session_list;
+
+gpg_error_t
+initialize_module_command (void)
+{
+ return 0;
+}
+
+static void
+finalize (ctrl_t ctrl)
+{
+ (void)ctrl;
+}
+
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ if (!strcmp (key, "event-signal"))
+ {
+ /* A value of 0 is allowed to reset the event signal. */
+#ifdef HAVE_W32_SYSTEM
+ if (!*value)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+#ifdef _WIN64
+ ctrl->server_local->event_signal = (void *)strtoull (value, NULL, 16);
+#else
+ ctrl->server_local->event_signal = (void *)strtoul (value, NULL, 16);
+#endif
+#else
+ int i = *value? atoi (value) : -1;
+ if (i < 0)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+ ctrl->server_local->event_signal = i;
+#endif
+ }
+
+ return 0;
+}
+
+#if 0
+static gpg_error_t
+pin_cb (void *opaque, const char *info, char **retstr)
+{
+ assuan_context_t ctx = opaque;
+ char *command;
+ int rc;
+ unsigned char *value;
+ size_t valuelen;
+
+ if (!retstr)
+ {
+ /* We prompt for pinpad entry. To make sure that the popup has
+ been show we use an inquire and not just a status message.
+ We ignore any value returned. */
+ if (info)
+ {
+ log_debug ("prompting for pinpad entry '%s'\n", info);
+ rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info);
+ if (rc < 0)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ xfree (command);
+ }
+ else
+ {
+ log_debug ("dismiss pinpad entry prompt\n");
+ rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT",
+ &value, &valuelen, MAXLEN_PIN);
+ }
+ if (!rc)
+ xfree (value);
+ return rc;
+ }
+
+ *retstr = NULL;
+ log_debug ("asking for PIN '%s'\n", info);
+
+ rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
+ if (rc < 0)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ /* Fixme: Write an inquire function which returns the result in
+ secure memory and check all further handling of the PIN. */
+ assuan_begin_confidential (ctx);
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ assuan_end_confidential (ctx);
+ xfree (command);
+ if (rc)
+ return rc;
+
+ if (!valuelen || value[valuelen-1])
+ {
+ /* We require that the returned value is an UTF-8 string */
+ xfree (value);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+ *retstr = (char*)value;
+ return 0;
+}
+#endif
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multi purpose command to return certain information. \n"
+ "Supported values of WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " socket_name - Return the name of the socket.\n"
+ " connections - Return number of active connections.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ int rc = 0;
+ const char *s;
+
+ if (!strcmp (line, "version"))
+ {
+ s = VERSION;
+ rc = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strcmp (line, "socket_name"))
+ {
+ s = tkd_get_socket_name ();
+ if (s)
+ rc = assuan_send_data (ctx, s, strlen (s));
+ else
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ }
+ else if (!strcmp (line, "connections"))
+ {
+ char numbuf[20];
+
+ snprintf (numbuf, sizeof numbuf, "%d", get_active_connection_count ());
+ rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else
+ rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+ return rc;
+}
+
+
+static const char hlp_restart[] =
+ "RESTART\n"
+ "\n"
+ "Restart the current connection.\n"
+ "\n"
+ "This is used by gpg-agent to reuse a primary pipe connection.";
+/*
+ * TKDeamon does not have a context for a connection (for now).
+ * So, this command does nothing.
+ */
+static gpg_error_t
+cmd_restart (assuan_context_t ctx, char *line)
+{
+ (void)line;
+ (void)ctx;
+ return 0;
+}
+
+
+/* SLOTLIST command
+ * A command to (re)scan for available keys, something like SERIALNO
+ * command of scdaemon.
+ */
+static const char hlp_slotlist[] =
+ "SLOTLIST\n"
+ "\n"
+ "Return the status of each token using a status response. This\n"
+ "function should be used to check for the presence of tokens.";
+static gpg_error_t
+cmd_slotlist (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ line = skip_options (line);
+ (void)line;
+
+ err = tkd_init (ctrl, ctx, 1);
+ return err;
+}
+
+static const char hlp_readkey[] =
+ "READKEY [--info[-only]] <keygrip>\n"
+ "\n"
+ "Return the public key for the given KEYGRIP, as a standard\n"
+ "S-expression.";
+static gpg_error_t
+cmd_readkey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ const char *keygrip;
+
+ line = xtrystrdup (line); /* Need a copy of the line. */
+ if (!line)
+ return gpg_error_from_syserror ();
+
+ keygrip = skip_options (line);
+ if (strlen (keygrip) != 40)
+ err = gpg_error (GPG_ERR_INV_ID);
+
+ err = tkd_readkey (ctrl, ctx, keygrip);
+
+ xfree (line);
+ return err;
+}
+
+static const char hlp_pksign[] =
+ "PKSIGN [--hash=[sha{256,384,512}|none]] <keygrip>\n"
+ "\n"
+ "The --hash option is optional; the default is none.";
+static gpg_error_t
+cmd_pksign (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ int hash_algo;
+ const char *keygrip;
+ unsigned char *outdata;
+ size_t outdatalen;
+
+ if (has_option (line, "--hash=sha256"))
+ hash_algo = GCRY_MD_SHA256;
+ else if (has_option (line, "--hash=sha384"))
+ hash_algo = GCRY_MD_SHA384;
+ else if (has_option (line, "--hash=sha512"))
+ hash_algo = GCRY_MD_SHA512;
+ else if (has_option (line, "--hash=none"))
+ hash_algo = 0;
+ else if (!strstr (line, "--"))
+ hash_algo = 0;
+ else
+ return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
+
+ line = xtrystrdup (line); /* Need a copy of the line. */
+ if (!line)
+ return gpg_error_from_syserror ();
+
+ keygrip = skip_options (line);
+
+ if (strlen (keygrip) != 40)
+ err = gpg_error (GPG_ERR_INV_ID);
+
+ err = tkd_sign (ctrl, ctx, keygrip, hash_algo, &outdata, &outdatalen);
+ if (err)
+ {
+ log_error ("tkd_sign failed: %s\n", gpg_strerror (err));
+ }
+ else
+ {
+ err = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ }
+
+ xfree (line);
+ return err;
+}
+
+static const char hlp_killtkd[] =
+ "KILLTKD\n"
+ "\n"
+ "Commit suicide.";
+static gpg_error_t
+cmd_killtkd (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ ctrl->server_local->stopme = 1;
+ assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+ return 0;
+}
+
+
+static const char hlp_keyinfo[] =
+ "KEYINFO [--list[=auth|encr|sign]] [--data] <keygrip>\n"
+ "\n"
+ "Return information about the key specified by the KEYGRIP. If the\n"
+ "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n"
+ "--list is given the keygrip is ignored and information about all\n"
+ "available keys are returned. Capability may limit the listing.\n"
+ "Unless --data is given, the\n"
+ "information is returned as a status line using the format:\n"
+ "\n"
+ " KEYINFO <keygrip> T <serialno> <idstr> <usage>\n"
+ "\n"
+ "KEYGRIP is the keygrip.\n"
+ "\n"
+ "SERIALNO is an ASCII string with the serial number of the\n"
+ " smartcard. If the serial number is not known a single\n"
+ " dash '-' is used instead.\n"
+ "\n"
+ "IDSTR is a string used to distinguish keys on a smartcard. If it\n"
+ " is not known a dash is used instead.\n"
+ "\n"
+ "USAGE is a string of capabilities of the key, 's' for sign, \n"
+ "'e' for encryption, 'a' for auth, and 'c' for cert. If it is not\n"
+ "known a dash is used instead.\n"
+ "\n"
+ "More information may be added in the future.";
+static gpg_error_t
+cmd_keyinfo (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err;
+ int cap;
+ int opt_data;
+ const char *keygrip = NULL;
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ opt_data = has_option (line, "--data");
+
+ line = xtrystrdup (line); /* Need a copy of the line. */
+ if (!line)
+ return gpg_error_from_syserror ();
+
+ cap = 0;
+ if (has_option (line, "--list"))
+ cap = 0;
+ else if (has_option (line, "--list=sign"))
+ cap = GCRY_PK_USAGE_SIGN;
+ else if (has_option (line, "--list=encr"))
+ cap = GCRY_PK_USAGE_ENCR;
+ else if (has_option (line, "--list=auth"))
+ cap = GCRY_PK_USAGE_AUTH;
+ else
+ keygrip = skip_options (line);
+
+ err = tkd_keyinfo (ctrl, ctx, keygrip, opt_data, cap);
+
+ xfree (line);
+ return err;
+}
+
+
+/* Send a keyinfo string as used by the KEYGRIP_ACTION_SEND_DATA. If
+ * DATA is true the string is emitted as a data line, else as a status
+ * line. */
+void
+send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
+ const char *serialno, const char *idstr, const char *usage)
+{
+ char *string;
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ string = xtryasprintf ("%s T %s %s %s%s", keygrip_str,
+ serialno? serialno : "-",
+ idstr? idstr : "-",
+ usage? usage : "-",
+ data? "\n" : "");
+
+ if (!string)
+ return;
+
+ if (!data)
+ assuan_write_status (ctx, "KEYINFO", string);
+ else
+ assuan_send_data (ctx, string, strlen (string));
+
+ xfree (string);
+ return;
+}
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (assuan_context_t ctx)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "SLOTLIST", cmd_slotlist, hlp_slotlist },
+ { "READKEY", cmd_readkey, hlp_readkey },
+ { "PKSIGN", cmd_pksign, hlp_pksign },
+ { "KILLTKD", cmd_killtkd, hlp_killtkd },
+ { "KEYINFO", cmd_keyinfo, hlp_keyinfo },
+ { "GETINFO", cmd_getinfo, hlp_getinfo },
+ { "RESTART", cmd_restart, hlp_restart },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler,
+ table[i].help);
+ if (rc)
+ return rc;
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's token daemon ready");
+
+ assuan_register_option_handler (ctx, option_handler);
+ return 0;
+}
+
+
+/* Startup the server. If FD is given as -1 this is simple pipe
+ server, otherwise it is a regular server. Returns true if there
+ are no more active asessions. */
+int
+tkd_command_handler (ctrl_t ctrl, gnupg_fd_t fd)
+{
+ int rc;
+ assuan_context_t ctx = NULL;
+ int stopme;
+
+ rc = assuan_new (&ctx);
+ if (rc)
+ {
+ log_error ("failed to allocate assuan context: %s\n",
+ gpg_strerror (rc));
+ tkd_exit (2);
+ }
+
+ if (fd == GNUPG_INVALID_FD)
+ {
+ assuan_fd_t filedes[2];
+
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ rc = assuan_init_pipe_server (ctx, filedes);
+ }
+ else
+ {
+ rc = assuan_init_socket_server (ctx, fd,
+ ASSUAN_SOCKET_SERVER_ACCEPTED);
+ }
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ gpg_strerror(rc));
+ tkd_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to register commands with Assuan: %s\n",
+ gpg_strerror(rc));
+ tkd_exit (2);
+ }
+ assuan_set_pointer (ctx, ctrl);
+
+ /* Allocate and initialize the server object. Put it into the list
+ of active sessions. */
+ ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
+ ctrl->server_local->next_session = session_list;
+ session_list = ctrl->server_local;
+ ctrl->server_local->ctrl_backlink = ctrl;
+ ctrl->server_local->assuan_ctx = ctx;
+
+ /* Command processing loop. */
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
+ continue;
+ }
+ }
+
+ /* Cleanup. */
+ finalize (ctrl);
+
+ /* Release the server object. */
+ if (session_list == ctrl->server_local)
+ session_list = ctrl->server_local->next_session;
+ else
+ {
+ struct server_local_s *sl;
+
+ for (sl=session_list; sl->next_session; sl = sl->next_session)
+ if (sl->next_session == ctrl->server_local)
+ break;
+ if (!sl->next_session)
+ BUG ();
+ sl->next_session = ctrl->server_local->next_session;
+ }
+ stopme = ctrl->server_local->stopme;
+ xfree (ctrl->server_local);
+ ctrl->server_local = NULL;
+
+ /* Release the Assuan context. */
+ assuan_release (ctx);
+
+ if (stopme)
+ tkd_exit (0);
+
+ /* If there are no more sessions return true. */
+ return !session_list;
+}
+
+
+/* Send a line with status information via assuan and escape all given
+ buffers. The variable elements are pairs of (char *, size_t),
+ terminated with a (NULL, 0). */
+void
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ const unsigned char *value;
+ size_t valuelen;
+ char buf[950], *p;
+ size_t n;
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ while ( (value = va_arg (arg_ptr, const unsigned char *))
+ && n < DIM (buf)-2 )
+ {
+ valuelen = va_arg (arg_ptr, size_t);
+ if (!valuelen)
+ continue; /* empty buffer */
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+ {
+ if (*value == '+' || *value == '\"' || *value == '%'
+ || *value < ' ')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ n += 2;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ }
+ *p = 0;
+ assuan_write_status (ctx, keyword, buf);
+
+ va_end (arg_ptr);
+}
+
+
+/* Send a ready formatted status line via assuan. */
+gpg_error_t
+send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
+{
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ if (strchr (args, '\n'))
+ {
+ log_error ("error: LF detected in status line - not sending\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ return assuan_write_status (ctx, keyword, args);
+}
+
+
+/* This status functions expects a printf style format string. No
+ * filtering of the data is done instead the printf formatted data is
+ * send using assuan_send_status. */
+gpg_error_t
+send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...)
+{
+ gpg_error_t err;
+ va_list arg_ptr;
+ assuan_context_t ctx;
+
+ if (!ctrl || !ctrl->server_local || !(ctx = ctrl->server_local->assuan_ctx))
+ return 0;
+
+ va_start (arg_ptr, format);
+ err = vprint_assuan_status (ctx, keyword, format, arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}
diff --git a/tkd/pkcs11.c b/tkd/pkcs11.c
new file mode 100644
index 000000000..33c19ecdd
--- /dev/null
+++ b/tkd/pkcs11.c
@@ -0,0 +1,1407 @@
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "tkdaemon.h"
+
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "pkcs11.h"
+
+/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
+ * length needs to small compared to the maximum Assuan line length. */
+#define MAXLEN_PIN 100
+
+/* Maximum allowed total data size for VALUE. */
+#define MAXLEN_VALUE 4096
+
+#define ck_function_list _CK_FUNCTION_LIST
+#define ck_token_info _CK_TOKEN_INFO
+#define ck_attribute _CK_ATTRIBUTE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_slot_id_t CK_SLOT_ID
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_notification_t CK_NOTIFICATION
+#define ck_flags_t CK_FLAGS
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+/*
+ * d_list -> dev
+ * session -> key_list -> key
+ *
+ */
+
+/*
+ * Major use cases:
+ * a few keys (two or three at maximum)
+ * with a single device, which only has one slot.
+ *
+ * So, static fixed allocation is better.
+ */
+#define MAX_KEYS 10
+#define MAX_SLOTS 10
+
+enum key_type {
+ KEY_RSA,
+ KEY_EC,
+ KEY_EDDSA,
+};
+
+#define KEY_FLAG_VALID (1 << 0)
+#define KEY_FLAG_NO_PUBKEY (1 << 1)
+#define KEY_FLAG_USAGE_SIGN (1 << 2)
+#define KEY_FLAG_USAGE_DECRYPT (1 << 3)
+
+struct key {
+ struct token *token; /* Back pointer. */
+ unsigned long flags;
+ int key_type;
+ char keygrip[2*KEYGRIP_LEN+1];
+ gcry_sexp_t pubkey;
+ /* PKCS#11 interface */
+ unsigned char label[256];
+ unsigned long label_len;
+ unsigned char id[256];
+ unsigned long id_len;
+ ck_object_handle_t p11_keyid;
+ ck_mechanism_type_t mechanism;
+};
+
+struct token {
+ struct cryptoki *ck; /* Back pointer. */
+ int valid;
+ ck_slot_id_t slot_id;
+ int login_required;
+ ck_session_handle_t session;
+ int num_keys;
+ struct key key_list[MAX_KEYS];
+};
+
+struct cryptoki {
+ void *handle; /* DL handle to PKCS#11 Module. */
+ struct ck_function_list *f;
+ int num_slots;
+ struct token token_list[MAX_SLOTS];
+};
+
+/* Possibly, we will extend this to support multiple PKCS#11 modules.
+ * For now, it's only one.
+ */
+static struct cryptoki ck_instance[1];
+
+
+static long
+get_function_list (struct cryptoki *ck)
+{
+ unsigned long r = 0;
+ unsigned long (*p_func) (struct ck_function_list **);
+
+ p_func = (CK_C_GetFunctionList)dlsym (ck->handle, "C_GetFunctionList");
+ if (p_func == NULL)
+ {
+ return -1;
+ }
+ r = p_func (&ck->f);
+
+ if (r || ck->f == NULL)
+ {
+ return -1;
+ }
+
+ r = ck->f->C_Initialize (NULL);
+ if (r)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+get_slot_list (struct cryptoki *ck,
+ unsigned long *num_slot_p,
+ ck_slot_id_t *slot_list)
+{
+ unsigned long err = 0;
+
+ /* Scute requires first call with NULL, to rescan. */
+ err = ck->f->C_GetSlotList (TRUE, NULL, num_slot_p);
+ if (err)
+ return err;
+
+ err = ck->f->C_GetSlotList (TRUE, slot_list, num_slot_p);
+ if (err)
+ {
+ return err;
+ }
+
+ return 0;
+}
+
+static long
+get_token_info (struct token *token, struct ck_token_info *tk_info)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+ ck_slot_id_t slot_id = token->slot_id;
+
+ err = ck->f->C_GetTokenInfo (slot_id, tk_info);
+ if (err)
+ {
+ return err;
+ }
+
+ return 0;
+}
+
+/* XXX Implement some useful things to be notified... */
+struct p11dev {
+ int d;
+};
+
+static struct p11dev p11_priv;
+
+static unsigned long
+notify_cb (ck_session_handle_t session,
+ ck_notification_t event, void *application)
+{
+ struct p11dev *priv = application;
+
+ (void)priv;
+ (void)session;
+ (void)event;
+ (void)application;
+ return 0;
+}
+
+static long
+open_session (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+ ck_slot_id_t slot_id = token->slot_id;
+ ck_session_handle_t session_handle;
+ ck_flags_t session_flags;
+
+ session_flags = CKU_USER;
+ // session_flags = session_flags | CKF_RW_SESSION;
+ session_flags = session_flags | CKF_SERIAL_SESSION;
+
+ err = ck->f->C_OpenSession (slot_id, session_flags,
+ (void *)&p11_priv, notify_cb, &session_handle);
+ if (err)
+ {
+ log_debug ("open_session: %ld\n", err);
+ return -1;
+ }
+
+ token->session = session_handle;
+ token->valid = 1;
+ token->num_keys = 0;
+
+ return 0;
+}
+
+static long
+close_session (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ if (!token->valid)
+ return -1;
+
+ err = ck->f->C_CloseSession (token->session);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+login (struct token *token,
+ const unsigned char *pin, int pin_len)
+{
+ unsigned long err = 0;
+ unsigned long user_type = CKU_USER;
+ struct cryptoki *ck = token->ck;
+
+ err = ck->f->C_Login (token->session, user_type,
+ (unsigned char *)pin, pin_len);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+logout (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ err = ck->f->C_Logout (token->session);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+compute_keygrip_rsa (char *keygrip, gcry_sexp_t *r_pubkey,
+ const char *modulus, unsigned long modulus_len,
+ const char *exponent, unsigned long exponent_len)
+{
+ gpg_error_t err;
+ gcry_sexp_t s_pkey = NULL;
+ const char *format = "(public-key(rsa(n%b)(e%b)))";
+ unsigned char grip[20];
+
+ *r_pubkey = NULL;
+ err = gcry_sexp_build (&s_pkey, NULL, format,
+ (int)modulus_len, modulus,
+ (int)exponent_len, exponent);
+ if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
+ err = gpg_error (GPG_ERR_INTERNAL);
+ else
+ {
+ bin2hex (grip, 20, keygrip);
+ log_debug ("keygrip: %s\n", keygrip);
+ *r_pubkey = s_pkey;
+ }
+}
+
+static void
+compute_keygrip_ec (char *keygrip, gcry_sexp_t *r_pubkey,
+ const char *curve, const char *ecpoint,
+ unsigned long ecpoint_len)
+{
+ gpg_error_t err;
+ gcry_sexp_t s_pkey = NULL;
+ const char *format = "(public-key(ecc(curve %s)(q%b)))";
+ unsigned char grip[20];
+
+ *r_pubkey = NULL;
+ err = gcry_sexp_build (&s_pkey, NULL, format, curve, (int)ecpoint_len,
+ ecpoint);
+ if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
+ err = gpg_error (GPG_ERR_INTERNAL);
+ else
+ {
+ bin2hex (grip, 20, keygrip);
+ log_debug ("keygrip: %s\n", keygrip);
+ *r_pubkey = s_pkey;
+ }
+}
+
+
+static long
+examine_public_key (struct token *token, struct key *k, unsigned long keytype,
+ int update_keyid, ck_object_handle_t obj)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+ unsigned char modulus[1024];
+ unsigned char exponent[8];
+ unsigned char ecparams[256];
+ unsigned char ecpoint[256];
+ struct ck_attribute templ[3];
+ unsigned long mechanisms[3];
+ unsigned char supported;
+
+ if (keytype == CKK_RSA)
+ {
+ if (update_keyid)
+ k->p11_keyid = obj;
+ k->key_type = KEY_RSA;
+
+ templ[0].type = CKA_MODULUS;
+ templ[0].pValue = (void *)modulus;
+ templ[0].ulValueLen = sizeof (modulus);
+
+ templ[1].type = CKA_PUBLIC_EXPONENT;
+ templ[1].pValue = (void *)exponent;
+ templ[1].ulValueLen = sizeof (exponent);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 2);
+ if (err)
+ {
+ k->flags |= KEY_FLAG_NO_PUBKEY;
+ return 1;
+ }
+
+ k->flags |= KEY_FLAG_VALID;
+ k->flags &= ~KEY_FLAG_NO_PUBKEY;
+ if ((modulus[0] & 0x80))
+ {
+ memmove (modulus+1, modulus, templ[0].ulValueLen);
+ templ[0].ulValueLen++;
+ modulus[0] = 0;
+ }
+
+ /* Found a RSA key. */
+ log_debug ("RSA: %ld %ld\n",
+ templ[0].ulValueLen,
+ templ[1].ulValueLen);
+
+ compute_keygrip_rsa (k->keygrip, &k->pubkey,
+ modulus, templ[0].ulValueLen,
+ exponent, templ[1].ulValueLen);
+
+ k->mechanism = CKM_RSA_PKCS;
+ }
+ else if (keytype == CKK_EC)
+ {
+ char *curve_oid = NULL;
+ const char *curve;
+
+ if (update_keyid)
+ k->p11_keyid = obj;
+ k->key_type = KEY_EC;
+
+ templ[0].type = CKA_EC_PARAMS;
+ templ[0].pValue = ecparams;
+ templ[0].ulValueLen = sizeof (ecparams);
+
+ templ[1].type = CKA_EC_POINT;
+ templ[1].pValue = (void *)ecpoint;
+ templ[1].ulValueLen = sizeof (ecpoint);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 2);
+ if (err)
+ {
+ k->flags |= KEY_FLAG_NO_PUBKEY;
+ return 1;
+ }
+
+ k->flags |= KEY_FLAG_VALID;
+ k->flags &= ~KEY_FLAG_NO_PUBKEY;
+ /* Found an ECC key. */
+ log_debug ("ECC: %ld %ld\n",
+ templ[0].ulValueLen,
+ templ[1].ulValueLen);
+
+ curve_oid = openpgp_oidbuf_to_str (ecparams+1, templ[0].ulValueLen-1);
+ curve = openpgp_oid_to_curve (curve_oid, 1);
+ xfree (curve_oid);
+
+ compute_keygrip_ec (k->keygrip, &k->pubkey,
+ curve, ecpoint, templ[1].ulValueLen);
+
+ templ[0].type = CKA_ALLOWED_MECHANISMS;
+ templ[0].pValue = (void *)mechanisms;
+ templ[0].ulValueLen = sizeof (mechanisms);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
+ if (!err)
+ {
+ if (templ[0].ulValueLen)
+ {
+ /* Scute works well. */
+ log_debug ("mechanism: %lx %ld\n", mechanisms[0], templ[0].ulValueLen);
+ k->mechanism = mechanisms[0];
+ }
+ else
+ {
+ log_debug ("SoftHSMv2???");
+ k->mechanism = CKM_ECDSA;
+ }
+ }
+ else
+ {
+ /* Yubkey YKCS doesn't offer CKA_ALLOWED_MECHANISMS,
+ unfortunately. */
+ log_debug ("Yubikey???");
+ k->mechanism = CKM_ECDSA_SHA256;
+ }
+ }
+
+ templ[0].type = CKA_SIGN;
+ templ[0].pValue = (void *)&supported;
+ templ[0].ulValueLen = sizeof (supported);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
+ if (!err)
+ {
+ /* XXX: Scute has the attribute, but not set. */
+ k->flags |= KEY_FLAG_USAGE_SIGN;
+ }
+
+ templ[0].type = CKA_DECRYPT;
+ templ[0].pValue = (void *)&supported;
+ templ[0].ulValueLen = sizeof (supported);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
+ if (!err && supported)
+ {
+ k->flags |= KEY_FLAG_USAGE_DECRYPT;
+ }
+
+ return 0;
+}
+
+static long
+detect_private_keys (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ struct ck_attribute templ[8];
+
+ unsigned long class;
+ unsigned long keytype;
+
+ unsigned long cnt = 0;
+ ck_object_handle_t obj;
+
+ class = CKO_PRIVATE_KEY;
+ templ[0].type = CKA_CLASS;
+ templ[0].pValue = (void *)&class;
+ templ[0].ulValueLen = sizeof (class);
+
+ token->num_keys = 0;
+
+ err = ck->f->C_FindObjectsInit (token->session, templ, 1);
+ if (!err)
+ {
+ while (TRUE)
+ {
+ unsigned long any;
+ struct key *k = &token->key_list[cnt]; /* Allocate a key. */
+
+ k->token = token;
+ k->flags = 0;
+
+ /* Portable way to get objects... is get it one by one. */
+ err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
+ if (err || any == 0)
+ break;
+
+ templ[0].type = CKA_KEY_TYPE;
+ templ[0].pValue = &keytype;
+ templ[0].ulValueLen = sizeof (keytype);
+
+ templ[1].type = CKA_LABEL;
+ templ[1].pValue = (void *)k->label;
+ templ[1].ulValueLen = sizeof (k->label) - 1;
+
+ templ[2].type = CKA_ID;
+ templ[2].pValue = (void *)k->id;
+ templ[2].ulValueLen = sizeof (k->id) - 1;
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
+ if (err)
+ {
+ continue;
+ }
+
+ cnt++;
+
+ k->label_len = templ[1].ulValueLen;
+ k->label[k->label_len] = 0;
+ k->id_len = templ[2].ulValueLen;
+ k->id[k->id_len] = 0;
+
+ log_debug ("slot: %lx handle: %ld label: %s key_type: %ld id: %s\n",
+ token->slot_id, obj, k->label, keytype, k->id);
+
+ if (examine_public_key (token, k, keytype, 1, obj))
+ continue;
+ }
+
+ token->num_keys = cnt;
+ err = ck->f->C_FindObjectsFinal (token->session);
+ if (err)
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static long
+check_public_keys (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ struct ck_attribute templ[8];
+
+ unsigned char label[256];
+ unsigned long class;
+ unsigned long keytype;
+ unsigned char id[256];
+
+ ck_object_handle_t obj;
+ int i;
+
+ class = CKO_PUBLIC_KEY;
+ templ[0].type = CKA_CLASS;
+ templ[0].pValue = (void *)&class;
+ templ[0].ulValueLen = sizeof (class);
+
+ err = ck->f->C_FindObjectsInit (token->session, templ, 1);
+ if (!err)
+ {
+ while (TRUE)
+ {
+ unsigned long any;
+ struct key *k = NULL;
+
+ /* Portable way to get objects... is get it one by one. */
+ err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
+ if (err || any == 0)
+ break;
+
+ templ[0].type = CKA_LABEL;
+ templ[0].pValue = (void *)label;
+ templ[0].ulValueLen = sizeof (label);
+
+ templ[1].type = CKA_KEY_TYPE;
+ templ[1].pValue = &keytype;
+ templ[1].ulValueLen = sizeof (keytype);
+
+ templ[2].type = CKA_ID;
+ templ[2].pValue = (void *)id;
+ templ[2].ulValueLen = sizeof (id);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
+ if (err)
+ {
+ continue;
+ }
+
+ label[templ[0].ulValueLen] = 0;
+ id[templ[2].ulValueLen] = 0;
+
+ /* Locate matching private key. */
+ for (i = 0; i < token->num_keys; i++)
+ {
+ k = &token->key_list[i];
+
+ if ((k->flags & KEY_FLAG_NO_PUBKEY)
+ && k->label_len == templ[0].ulValueLen
+ && memcmp (label, k->label, k->label_len) == 0
+ && ((keytype == CKK_RSA && k->key_type == KEY_RSA)
+ || (keytype == CKK_EC && k->key_type == KEY_EC))
+ && k->id_len == templ[2].ulValueLen
+ && memcmp (id, k->id, k->id_len) == 0)
+ break;
+ }
+
+ if (i == token->num_keys)
+ continue;
+
+ log_debug ("pub: slot: %lx handle: %ld label: %s key_type: %ld id: %s\n",
+ token->slot_id, obj, label, keytype, id);
+
+ if (examine_public_key (token, k, keytype, 0, obj))
+ continue;
+ }
+
+ err = ck->f->C_FindObjectsFinal (token->session);
+ if (err)
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#if 0
+static long
+get_certificate (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ struct ck_attribute templ[1];
+
+ unsigned long class;
+ unsigned char certificate[4096];
+ unsigned long cert_len;
+ int certificate_available;
+
+ ck_object_handle_t obj;
+ int i;
+
+ class = CKO_CERTIFICATE;
+ templ[0].type = CKA_CLASS;
+ templ[0].pValue = (void *)&class;
+ templ[0].ulValueLen = sizeof (class);
+
+ err = ck->f->C_FindObjectsInit (token->session, templ, 1);
+ if (!err)
+ {
+ while (TRUE)
+ {
+ unsigned long any;
+
+ /* Portable way to get objects... is get it one by one. */
+ err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
+ if (err || any == 0)
+ break;
+
+ templ[0].type = CKA_VALUE;
+ templ[0].pValue = (void *)certificate;
+ templ[0].ulValueLen = sizeof (certificate);
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
+ if (err)
+ certificate_available = 0;
+ else
+ {
+ certificate_available = 1;
+ cert_len = templ[0].ulValueLen;
+
+ puts ("Certificate available:");
+ for (i = 0; i < cert_len; i++)
+ {
+ printf ("%02x", certificate[i]);
+ if ((i % 16) == 15)
+ puts ("");
+ }
+ puts ("");
+ }
+ }
+
+ err = ck->f->C_FindObjectsFinal (token->session);
+ if (err)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static long
+learn_keys (struct token *token)
+{
+ int i;
+
+ /* Detect private keys on the token.
+ * It's good if it also offers raw public key material.
+ */
+ detect_private_keys (token);
+
+ /*
+ * In some implementations (EC key on SoftHSMv2, for example),
+ * attributes for raw public key material is not available in
+ * a CKO_PRIVATE_KEY object.
+ *
+ * We try to examine CKO_PUBLIC_KEY objects, too see if it provides
+ * raw public key material in a CKO_PUBLIC_KEY object.
+ */
+ check_public_keys (token);
+
+ for (i = 0; i < token->num_keys; i++)
+ {
+ struct key *k = &token->key_list[i];
+
+ if ((k->flags & KEY_FLAG_NO_PUBKEY))
+ k->flags &= ~KEY_FLAG_NO_PUBKEY;
+ }
+
+#if 0
+ /* Another way to get raw public key material is get it from the
+ certificate, if available. */
+ get_certificate (token);
+#endif
+
+ return 0;
+}
+
+
+static long
+find_key (struct cryptoki *ck, const char *keygrip, struct key **r_key)
+{
+ int i;
+ int j;
+
+ log_debug ("find_key: %s\n", keygrip);
+
+ *r_key = NULL;
+ for (i = 0; i < ck->num_slots; i++)
+ {
+ struct token *token = &ck->token_list[i];
+
+ if (!token->valid)
+ continue;
+
+ for (j = 0; j < token->num_keys; j++)
+ {
+ struct key *k = &token->key_list[j];
+
+ if ((k->flags & KEY_FLAG_VALID) == 0)
+ continue;
+
+ if (memcmp (k->keygrip, keygrip, 40) == 0)
+ {
+ *r_key = k;
+ log_debug ("found a key at %d:%d\n", i, j);
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+struct iter_key {
+ struct cryptoki *ck;
+ int i;
+ int j;
+ unsigned long mask;
+ int st;
+};
+
+static void
+iter_find_key_setup (struct iter_key *iter, struct cryptoki *ck, int cap)
+{
+ iter->st = 0;
+ iter->ck = ck;
+ iter->i = 0;
+ iter->j = 0;
+ iter->mask = 0;
+ if (cap == GCRY_PK_USAGE_SIGN)
+ iter->mask |= KEY_FLAG_USAGE_SIGN;
+ else if (cap == GCRY_PK_USAGE_ENCR)
+ iter->mask = KEY_FLAG_USAGE_DECRYPT;
+ else
+ iter->mask = KEY_FLAG_USAGE_SIGN | KEY_FLAG_USAGE_DECRYPT;
+}
+
+static int
+iter_find_key (struct iter_key *iter, struct key **r_key)
+{
+ struct cryptoki *ck = iter->ck;
+ struct token *token;
+ struct key *k;
+
+ *r_key = NULL;
+
+ if (iter->i < ck->num_slots)
+ token = &ck->token_list[iter->i];
+ else
+ token = NULL;
+
+ switch (iter->st)
+ while (1)
+ {
+ case 0:
+ if (iter->i < ck->num_slots)
+ {
+ token = &ck->token_list[iter->i++];
+ if (!token->valid)
+ continue;
+ }
+ else
+ {
+ iter->st = 2;
+ /*FALLTHROUGH*/
+ default:
+ return 0;
+ }
+
+ iter->j = 0;
+ while (1)
+ {
+ /*FALLTHROUGH*/
+ case 1:
+ if (token && iter->j < token->num_keys)
+ {
+ k = &token->key_list[iter->j++];
+ if ((k->flags & KEY_FLAG_VALID) && (k->flags & iter->mask))
+ {
+ /* Found */
+ *r_key = k;
+ iter->st = 1;
+ return 1;
+ }
+ }
+ else
+ break;
+ }
+ }
+}
+
+static gpg_error_t
+setup_pksign (struct key *key, int hash_algo,
+ unsigned char **r_signature, unsigned long *r_signature_len)
+{
+ gpg_error_t err = 0;
+ unsigned long r = 0;
+ struct token *token = key->token;
+ struct cryptoki *ck = token->ck;
+ ck_mechanism_type_t mechanism;
+ struct ck_mechanism mechanism_struct;
+ unsigned int nbits;
+ unsigned long siglen;
+ unsigned char *sig;
+
+ nbits = gcry_pk_get_nbits (key->pubkey);
+
+ mechanism = key->mechanism;
+ if (key->key_type == KEY_RSA)
+ {
+ /* It's CKM_RSA_PKCS, it requires that hash algo OID included in
+ the data to be signed. */
+ if (!hash_algo)
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+
+ siglen = (nbits+7)/8;
+ }
+ else if (key->key_type == KEY_EC)
+ {
+ siglen = ((nbits+7)/8) * 2;
+ }
+ else if (key->key_type == KEY_EDDSA)
+ {
+ mechanism = CKM_EDDSA;
+ siglen = ((nbits+7)/8)*2;
+ }
+ else
+ return gpg_error (GPG_ERR_BAD_SECKEY);
+
+ mechanism_struct.mechanism = mechanism;
+ mechanism_struct.parameter = NULL;
+ mechanism_struct.parameter_len = 0;
+
+ r = ck->f->C_SignInit (token->session, &mechanism_struct,
+ key->p11_keyid);
+ if (r)
+ {
+ log_error ("C_SignInit error: %ld", r);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ sig = xtrymalloc (siglen);
+ if (!sig)
+ return gpg_error_from_syserror ();
+
+ *r_signature = sig;
+ *r_signature_len = siglen;
+
+ return err;
+}
+
+static gpg_error_t
+do_pksign (struct key *key, int hash_algo,
+ const unsigned char *u_data, unsigned long u_data_len,
+ unsigned char *signature,
+ unsigned long *r_signature_len)
+{
+ gpg_error_t err = 0;
+ unsigned long r = 0;
+ struct token *token = key->token;
+ struct cryptoki *ck = token->ck;
+ ck_mechanism_type_t mechanism;
+ unsigned char data[1024];
+ unsigned long data_len;
+
+ mechanism = key->mechanism;
+ if (key->key_type == KEY_RSA)
+ {
+ size_t asnlen = sizeof (data);
+
+ gcry_md_get_asnoid (hash_algo, data, &asnlen);
+ /* u_data_len == gcry_md_get_algo_dlen (hash_algo) */
+ memcpy (data+asnlen, u_data, u_data_len);
+ data_len = asnlen+gcry_md_get_algo_dlen (hash_algo);
+ }
+ else if (key->key_type == KEY_EC)
+ {
+ if (mechanism == CKM_ECDSA)
+ {
+ /* SoftHSMv2 */
+ memcpy (data, u_data, u_data_len);
+ data_len = u_data_len;
+ }
+ else
+ {
+ if (!hash_algo)
+ {
+ /* Not specified by user, determine from MECHANISM */
+ if (mechanism == CKM_ECDSA_SHA256)
+ hash_algo = GCRY_MD_SHA256;
+ else if (mechanism == CKM_ECDSA_SHA384)
+ hash_algo = GCRY_MD_SHA384;
+ else if (mechanism == CKM_ECDSA_SHA384)
+ hash_algo = GCRY_MD_SHA512;
+ else
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
+
+ /* Scute, YKCS11 */
+ /* u_data_len == gcry_md_get_algo_dlen (hash_algo) */
+ memcpy (data, u_data, u_data_len);
+ data_len = gcry_md_get_algo_dlen (hash_algo);
+ }
+ }
+ else if (key->key_type == KEY_EDDSA)
+ {
+ mechanism = CKM_EDDSA;
+ memcpy (data, u_data, u_data_len);
+ data_len = u_data_len;
+ }
+ else
+ return gpg_error (GPG_ERR_BAD_SECKEY);
+
+ r = ck->f->C_Sign (token->session,
+ data, data_len,
+ signature, r_signature_len);
+ if (r)
+ {
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ return err;
+}
+
+static gpg_error_t
+token_open (assuan_context_t ctx, struct cryptoki *ck, struct token *token,
+ ck_slot_id_t slot_id)
+{
+ gpg_error_t err = 0;
+ struct ck_token_info tk_info;
+ long r;
+
+ token->ck = ck;
+ token->valid = 0;
+ token->slot_id = slot_id;
+
+ if (get_token_info (token, &tk_info))
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+
+ if ((tk_info.flags & CKF_TOKEN_INITIALIZED) == 0
+ || (tk_info.flags & CKF_TOKEN_PRESENT) == 0
+ || (tk_info.flags & CKF_USER_PIN_LOCKED) != 0)
+ return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+
+ token->login_required = (tk_info.flags & CKF_LOGIN_REQUIRED);
+
+ r = open_session (token);
+ if (r)
+ {
+ log_error ("Error at open_session: %ld\n", r);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ if (token->login_required)
+ {
+ char *command;
+ int rc;
+ unsigned char *value;
+ size_t valuelen;
+
+ log_debug ("asking for PIN '%ld'\n", token->slot_id);
+
+ rc = gpgrt_asprintf (&command, "NEEDPIN %ld", token->slot_id);
+ if (rc < 0)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ assuan_end_confidential (ctx);
+ xfree (command);
+ if (err)
+ {
+ close_session (token);
+ token->session = 0;
+ return err;
+ }
+
+ login (token, value, valuelen);
+ xfree (value);
+ }
+
+ r = learn_keys (token);
+ return 0;
+}
+
+static gpg_error_t
+token_close (struct token *token)
+{
+ int j;
+ long r;
+ int num_keys = token->num_keys;
+
+ if (!token->valid)
+ return 0;
+
+ if (token->login_required)
+ logout (token);
+
+ r = close_session (token);
+ if (r)
+ log_error ("Error at close_session: %ld\n", r);
+
+ token->ck = NULL;
+ token->slot_id = 0;
+ token->login_required = 0;
+ token->session = 0;
+ token->num_keys = 0;
+
+ for (j = 0; j < num_keys; j++)
+ {
+ struct key *k = &token->key_list[j];
+
+ if ((k->flags & KEY_FLAG_VALID))
+ {
+ gcry_sexp_release (k->pubkey);
+ k->pubkey = NULL;
+ }
+
+ k->token = NULL;
+ k->flags = 0;
+ k->key_type = 0;
+ k->label_len = 0;
+ k->id_len = 0;
+ k->p11_keyid = 0;
+ k->mechanism = 0;
+ }
+
+ token->valid = 0;
+ return 0;
+}
+
+
+static gpg_error_t
+token_check (struct token *token)
+{
+ struct ck_token_info tk_info;
+
+ if (get_token_info (token, &tk_info))
+ {
+ /* Possibly, invalidate the token and close session.
+ * Now, ingore the error. */
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ if ((tk_info.flags & CKF_TOKEN_INITIALIZED) == 0
+ || (tk_info.flags & CKF_TOKEN_PRESENT) == 0
+ || (tk_info.flags & CKF_USER_PIN_LOCKED) != 0)
+ {
+ token_close (token);
+ return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+ }
+
+ return 0;
+}
+
+
+gpg_error_t
+tkd_init (ctrl_t ctrl, assuan_context_t ctx, int rescan)
+{
+ gpg_error_t err = 0;
+
+ long r;
+ struct cryptoki *ck = ck_instance;
+ unsigned long num_slots = MAX_SLOTS;
+ ck_slot_id_t slot_list[MAX_SLOTS];
+ int i;
+
+ const char *module_name;
+
+ module_name = opt.pkcs11_driver;
+ if (!module_name)
+ return gpg_error (GPG_ERR_NO_NAME);
+
+ if (ck->handle == NULL)
+ {
+ void *handle;
+ int num_tokens = 0;
+
+ handle = dlopen (module_name, RTLD_NOW);
+ if (handle == NULL)
+ {
+ return -1;
+ }
+
+ ck->handle = handle;
+
+ r = get_function_list (ck);
+ if (r)
+ {
+ dlclose (ck->handle);
+ ck->handle = NULL;
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ r = get_slot_list (ck, &num_slots, slot_list);
+ if (r)
+ {
+ dlclose (ck->handle);
+ ck->handle = NULL;
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ for (i = 0; i < num_slots; i++)
+ {
+ struct token *token = &ck->token_list[num_tokens]; /* Allocate one token in CK */
+
+ err = token_open (ctx, ck, token, slot_list[i]);
+ if (!err)
+ num_tokens++;
+ }
+
+ ck->num_slots = num_tokens;
+ return 0;
+ }
+ else if (rescan == 0)
+ return 0;
+
+ /* Rescan the slots to see the changes. */
+
+ r = get_slot_list (ck, &num_slots, slot_list);
+ if (r)
+ {
+ tkd_fini (ctrl, ctx);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+
+ for (i = 0; i < num_slots; i++)
+ {
+ int j;
+ ck_slot_id_t slot_id = slot_list[i];
+ struct token *token = NULL;
+
+ for (j = 0; j < ck->num_slots; j++)
+ if (slot_id == ck->token_list[j].slot_id)
+ {
+ token = &ck->token_list[j];
+ break;
+ }
+
+ if (token)
+ {
+ err = token_check (token);
+ }
+ else
+ /* new token */
+ {
+ /* Allocate one token in CK */
+ token = &ck->token_list[ck->num_slots];
+ err = token_open (ctx, ck, token, slot_id);
+ if (!err)
+ ck->num_slots++;
+ }
+ }
+
+ return err;
+}
+
+gpg_error_t
+tkd_fini (ctrl_t ctrl, assuan_context_t ctx)
+{
+ long r;
+ struct cryptoki *ck = ck_instance;
+ int i;
+
+ (void)ctrl;
+ (void)ctx;
+
+ for (i = 0; i < ck->num_slots; i++)
+ {
+ struct token *token = &ck->token_list[i];
+
+ token_close (token);
+ }
+
+ ck->num_slots = 0;
+
+ r = ck->f->C_Finalize (NULL);
+ if (r)
+ {
+ return -1;
+ }
+
+ ck->f = NULL;
+
+ dlclose (ck->handle);
+ ck->handle = NULL;
+
+ return 0;
+}
+
+
+gpg_error_t
+tkd_sign (ctrl_t ctrl, assuan_context_t ctx,
+ const char *keygrip, int hash_algo,
+ unsigned char **r_outdata, size_t *r_outdatalen)
+{
+ gpg_error_t err;
+ struct key *k;
+ struct cryptoki *ck = ck_instance;
+ unsigned long r;
+
+ (void)ctrl;
+ /* mismatch: size_t for GnuPG, unsigned long for PKCS#11 */
+ /* mismatch: application prepare buffer for PKCS#11 */
+
+ if (!ck->handle)
+ {
+ err = tkd_init (ctrl, ctx, 0);
+ if (err)
+ return err;
+ }
+
+ *r_outdata = NULL;
+ r = find_key (ck, keygrip, &k);
+ if (r)
+ return gpg_error (GPG_ERR_NO_SECKEY);
+ else
+ {
+ const char *cmd;
+ unsigned char *value;
+ size_t valuelen;
+ unsigned char *sig = NULL;
+
+ err = setup_pksign (k, hash_algo, &sig, r_outdatalen);
+ if (err)
+ return err;
+
+ cmd = "EXTRA";
+ err = assuan_inquire (ctx, cmd, &value, &valuelen, MAXLEN_VALUE);
+ if (err)
+ {
+ xfree (sig);
+ return err;
+ }
+
+ err = do_pksign (k, hash_algo, value, valuelen, sig, r_outdatalen);
+ wipememory (value, valuelen);
+ xfree (value);
+ if (err)
+ {
+ xfree (sig);
+ return err;
+ }
+
+ *r_outdata = sig;
+ }
+
+ return err;
+}
+
+static const char *
+get_usage_string (struct key *k)
+{
+ const char *usage = NULL;
+
+ if ((k->flags & KEY_FLAG_USAGE_SIGN))
+ {
+ if ((k->flags & KEY_FLAG_USAGE_DECRYPT))
+ usage = "se";
+ else
+ usage = "s";
+ }
+ else
+ {
+ if ((k->flags & KEY_FLAG_USAGE_DECRYPT))
+ usage = "e";
+ else
+ usage = "-";
+ }
+
+ return usage;
+}
+
+gpg_error_t
+tkd_readkey (ctrl_t ctrl, assuan_context_t ctx, const char *keygrip)
+{
+ gpg_error_t err = 0;
+ struct key *k;
+ struct cryptoki *ck = ck_instance;
+ unsigned long r;
+ unsigned char *pk;
+ size_t pklen;
+
+ (void)ctrl;
+ (void)ctx;
+
+ if (!ck->handle)
+ {
+ err = tkd_init (ctrl, ctx, 0);
+ if (err)
+ return err;
+ }
+
+ r = find_key (ck, keygrip, &k);
+ if (r)
+ return gpg_error (GPG_ERR_NO_SECKEY);
+
+ pklen = gcry_sexp_sprint (k->pubkey, GCRYSEXP_FMT_CANON, NULL, 0);
+ pk = xtrymalloc (pklen);
+ if (!pk)
+ {
+ return gpg_error_from_syserror ();
+ }
+ gcry_sexp_sprint (k->pubkey, GCRYSEXP_FMT_CANON, pk, pklen);
+ err = assuan_send_data (ctx, pk, pklen);
+ xfree (pk);
+ return err;
+}
+
+gpg_error_t
+tkd_keyinfo (ctrl_t ctrl, assuan_context_t ctx, const char *keygrip,
+ int opt_data, int cap)
+{
+ gpg_error_t err = 0;
+ struct cryptoki *ck = ck_instance;
+ struct key *k;
+ const char *usage;
+
+ if (!ck->handle)
+ {
+ err = tkd_init (ctrl, ctx, 0);
+ if (err)
+ return err;
+ }
+
+ if (keygrip)
+ {
+ unsigned long r;
+
+ r = find_key (ck, keygrip, &k);
+ if (r)
+ return gpg_error (GPG_ERR_NO_SECKEY);
+
+ usage = get_usage_string (k);
+ send_keyinfo (ctrl, opt_data, keygrip,
+ k->label_len ? (const char *)k->label : "-",
+ k->id_len ? (const char *)k->id : "-",
+ usage);
+ }
+ else
+ {
+ struct iter_key iter;
+
+ iter_find_key_setup (&iter, ck, cap);
+ while (iter_find_key (&iter, &k))
+ {
+ usage = get_usage_string (k);
+ send_keyinfo (ctrl, opt_data, k->keygrip,
+ k->label_len ? (const char *)k->label : "-",
+ k->id_len ? (const char *)k->id : "-",
+ usage);
+ }
+ }
+
+ return err;
+}
diff --git a/tkd/pkcs11.h b/tkd/pkcs11.h
new file mode 100644
index 000000000..12c94441d
--- /dev/null
+++ b/tkd/pkcs11.h
@@ -0,0 +1,1384 @@
+/* pkcs11.h
+ * Copyright 2006, 2007 g10 Code GmbH
+ * Copyright 2006 Andreas Jellinghaus
+ *
+ * This file is free software; as a special exception the authors give
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ * SPDX-License-Identifier: FSFULLR
+ */
+
+/* This file is a modified implementation of the PKCS #11 standard by
+ * RSA Security Inc. It is mostly a drop-in replacement, with the
+ * following change:
+ *
+ * This header file does not require any macro definitions by the user
+ * (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros
+ * for you (if useful, some are missing, let me know if you need
+ * more).
+ *
+ * There is an additional API available that does comply better to the
+ * GNU coding standard. It can be switched on by defining
+ * CRYPTOKI_GNU before including this header file. For this, the
+ * following changes are made to the specification:
+ *
+ * All structure types are changed to a "struct ck_foo" where CK_FOO
+ * is the type name in PKCS #11.
+ *
+ * All non-structure types are changed to ck_foo_t where CK_FOO is the
+ * lowercase version of the type name in PKCS #11. The basic types
+ * (CK_ULONG et al.) are removed without substitute.
+ *
+ * All members of structures are modified in the following way: Type
+ * indication prefixes are removed, and underscore characters are
+ * inserted before words. Then the result is lowercased.
+ *
+ * Note that function names are still in the original case, as they
+ * need for ABI compatibility.
+ *
+ * CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use
+ * <stdbool.h>.
+ *
+ * If CRYPTOKI_COMPAT is defined before including this header file,
+ * then none of the API changes above take place, and the API is the
+ * one defined by the PKCS #11 standard.
+ *
+ *
+ * Please submit changes back to the Scute project with a request to
+ * https://dev.gnupg.org, so that they can be picked up by other
+ * projects from there as well.
+ */
+
+
+#ifndef PKCS11_H
+#define PKCS11_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The version of cryptoki we implement. The revision is changed with
+ each modification of this file. If you do not use the "official"
+ version of this file, please consider deleting the revision macro
+ (you may use a macro with a different name to keep track of your
+ versions). */
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 20
+#define CRYPTOKI_VERSION_REVISION 6
+
+
+/* Compatibility interface is default, unless CRYPTOKI_GNU is
+ given. */
+#ifndef CRYPTOKI_GNU
+#ifndef CRYPTOKI_COMPAT
+#define CRYPTOKI_COMPAT 1
+#endif
+#endif
+
+/* System dependencies. */
+
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+
+/* There is a matching pop below. */
+#pragma pack(push, cryptoki, 1)
+
+#ifdef CRYPTOKI_EXPORTS
+#define CK_SPEC __declspec(dllexport)
+#else
+#define CK_SPEC __declspec(dllimport)
+#endif
+
+#else
+
+#define CK_SPEC
+
+#endif
+
+
+#ifdef CRYPTOKI_COMPAT
+ /* If we are in compatibility mode, switch all exposed names to the
+ PKCS #11 variant. There are corresponding #undefs below. */
+
+#define ck_flags_t CK_FLAGS
+#define ck_version _CK_VERSION
+
+#define ck_info _CK_INFO
+#define cryptoki_version cryptokiVersion
+#define manufacturer_id manufacturerID
+#define library_description libraryDescription
+#define library_version libraryVersion
+
+#define ck_notification_t CK_NOTIFICATION
+#define ck_slot_id_t CK_SLOT_ID
+
+#define ck_slot_info _CK_SLOT_INFO
+#define slot_description slotDescription
+#define hardware_version hardwareVersion
+#define firmware_version firmwareVersion
+
+#define ck_token_info _CK_TOKEN_INFO
+#define serial_number serialNumber
+#define max_session_count ulMaxSessionCount
+#define session_count ulSessionCount
+#define max_rw_session_count ulMaxRwSessionCount
+#define rw_session_count ulRwSessionCount
+#define max_pin_len ulMaxPinLen
+#define min_pin_len ulMinPinLen
+#define total_public_memory ulTotalPublicMemory
+#define free_public_memory ulFreePublicMemory
+#define total_private_memory ulTotalPrivateMemory
+#define free_private_memory ulFreePrivateMemory
+#define utc_time utcTime
+
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_user_type_t CK_USER_TYPE
+#define ck_state_t CK_STATE
+
+#define ck_session_info _CK_SESSION_INFO
+#define slot_id slotID
+#define device_error ulDeviceError
+
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_object_class_t CK_OBJECT_CLASS
+#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
+#define ck_key_type_t CK_KEY_TYPE
+#define ck_certificate_type_t CK_CERTIFICATE_TYPE
+#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
+
+#define ck_attribute _CK_ATTRIBUTE
+#define value pValue
+#define value_len ulValueLen
+
+#define ck_date _CK_DATE
+
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_mechanism_info _CK_MECHANISM_INFO
+#define min_key_size ulMinKeySize
+#define max_key_size ulMaxKeySize
+
+#define ck_rv_t CK_RV
+#define ck_notify_t CK_NOTIFY
+
+#define ck_function_list _CK_FUNCTION_LIST
+
+#define ck_createmutex_t CK_CREATEMUTEX
+#define ck_destroymutex_t CK_DESTROYMUTEX
+#define ck_lockmutex_t CK_LOCKMUTEX
+#define ck_unlockmutex_t CK_UNLOCKMUTEX
+
+#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
+#define create_mutex CreateMutex
+#define destroy_mutex DestroyMutex
+#define lock_mutex LockMutex
+#define unlock_mutex UnlockMutex
+#define reserved pReserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+
+typedef unsigned long ck_flags_t;
+
+struct ck_version
+{
+ unsigned char major;
+ unsigned char minor;
+};
+
+
+struct ck_info
+{
+ struct ck_version cryptoki_version;
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ unsigned char library_description[32];
+ struct ck_version library_version;
+};
+
+
+typedef unsigned long ck_notification_t;
+
+#define CKN_SURRENDER (0UL)
+
+
+typedef unsigned long ck_slot_id_t;
+
+
+struct ck_slot_info
+{
+ unsigned char slot_description[64];
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+};
+
+
+#define CKF_TOKEN_PRESENT (1UL << 0)
+#define CKF_REMOVABLE_DEVICE (1UL << 1)
+#define CKF_HW_SLOT (1UL << 2)
+#define CKF_ARRAY_ATTRIBUTE (1UL << 30)
+
+
+struct ck_token_info
+{
+ unsigned char label[32];
+ unsigned char manufacturer_id[32];
+ unsigned char model[16];
+ unsigned char serial_number[16];
+ ck_flags_t flags;
+ unsigned long max_session_count;
+ unsigned long session_count;
+ unsigned long max_rw_session_count;
+ unsigned long rw_session_count;
+ unsigned long max_pin_len;
+ unsigned long min_pin_len;
+ unsigned long total_public_memory;
+ unsigned long free_public_memory;
+ unsigned long total_private_memory;
+ unsigned long free_private_memory;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+ unsigned char utc_time[16];
+};
+
+
+#define CKF_RNG (1UL << 0)
+#define CKF_WRITE_PROTECTED (1UL << 1)
+#define CKF_LOGIN_REQUIRED (1UL << 2)
+#define CKF_USER_PIN_INITIALIZED (1UL << 3)
+#define CKF_RESTORE_KEY_NOT_NEEDED (1UL << 5)
+#define CKF_CLOCK_ON_TOKEN (1UL << 6)
+#define CKF_PROTECTED_AUTHENTICATION_PATH (1UL << 8)
+#define CKF_DUAL_CRYPTO_OPERATIONS (1UL << 9)
+#define CKF_TOKEN_INITIALIZED (1UL << 10)
+#define CKF_SECONDARY_AUTHENTICATION (1UL << 11)
+#define CKF_USER_PIN_COUNT_LOW (1UL << 16)
+#define CKF_USER_PIN_FINAL_TRY (1UL << 17)
+#define CKF_USER_PIN_LOCKED (1UL << 18)
+#define CKF_USER_PIN_TO_BE_CHANGED (1UL << 19)
+#define CKF_SO_PIN_COUNT_LOW (1UL << 20)
+#define CKF_SO_PIN_FINAL_TRY (1UL << 21)
+#define CKF_SO_PIN_LOCKED (1UL << 22)
+#define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23)
+
+#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
+#define CK_EFFECTIVELY_INFINITE (0UL)
+
+
+typedef unsigned long ck_session_handle_t;
+
+#define CK_INVALID_HANDLE (0UL)
+
+
+typedef unsigned long ck_user_type_t;
+
+#define CKU_SO (0UL)
+#define CKU_USER (1UL)
+#define CKU_CONTEXT_SPECIFIC (2UL)
+
+
+typedef unsigned long ck_state_t;
+
+#define CKS_RO_PUBLIC_SESSION (0UL)
+#define CKS_RO_USER_FUNCTIONS (1UL)
+#define CKS_RW_PUBLIC_SESSION (2UL)
+#define CKS_RW_USER_FUNCTIONS (3UL)
+#define CKS_RW_SO_FUNCTIONS (4UL)
+
+
+struct ck_session_info
+{
+ ck_slot_id_t slot_id;
+ ck_state_t state;
+ ck_flags_t flags;
+ unsigned long device_error;
+};
+
+#define CKF_RW_SESSION (1UL << 1)
+#define CKF_SERIAL_SESSION (1UL << 2)
+
+
+typedef unsigned long ck_object_handle_t;
+
+
+typedef unsigned long ck_object_class_t;
+
+#define CKO_DATA (0UL)
+#define CKO_CERTIFICATE (1UL)
+#define CKO_PUBLIC_KEY (2UL)
+#define CKO_PRIVATE_KEY (3UL)
+#define CKO_SECRET_KEY (4UL)
+#define CKO_HW_FEATURE (5UL)
+#define CKO_DOMAIN_PARAMETERS (6UL)
+#define CKO_MECHANISM (7UL)
+#define CKO_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_hw_feature_type_t;
+
+#define CKH_MONOTONIC_COUNTER (1UL)
+#define CKH_CLOCK (2UL)
+#define CKH_USER_INTERFACE (3UL)
+#define CKH_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_key_type_t;
+
+#define CKK_RSA (0UL)
+#define CKK_DSA (1UL)
+#define CKK_DH (2UL)
+#define CKK_EC (3UL)
+#define CKK_X9_42_DH (4UL)
+#define CKK_KEA (5UL)
+#define CKK_GENERIC_SECRET (0x10UL)
+#define CKK_RC2 (0x11UL)
+#define CKK_RC4 (0x12UL)
+#define CKK_DES (0x13UL)
+#define CKK_DES2 (0x14UL)
+#define CKK_DES3 (0x15UL)
+#define CKK_CAST (0x16UL)
+#define CKK_CAST3 (0x17UL)
+#define CKK_CAST128 (0x18UL)
+#define CKK_RC5 (0x19UL)
+#define CKK_IDEA (0x1aUL)
+#define CKK_SKIPJACK (0x1bUL)
+#define CKK_BATON (0x1cUL)
+#define CKK_JUNIPER (0x1dUL)
+#define CKK_CDMF (0x1eUL)
+#define CKK_AES (0x1fUL)
+#define CKK_BLOWFISH (0x20UL)
+#define CKK_TWOFISH (0x21UL)
+#define CKK_GOSTR3410 (0x30UL)
+#define CKK_EC_EDWARDS (0x40UL)
+#define CKK_EC_MONTGOMERY (0x41UL)
+#define CKK_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_certificate_type_t;
+
+#define CKC_X_509 (0UL)
+#define CKC_X_509_ATTR_CERT (1UL)
+#define CKC_WTLS (2UL)
+#define CKC_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_attribute_type_t;
+
+#define CKA_CLASS (0UL)
+#define CKA_TOKEN (1UL)
+#define CKA_PRIVATE (2UL)
+#define CKA_LABEL (3UL)
+#define CKA_APPLICATION (0x10UL)
+#define CKA_VALUE (0x11UL)
+#define CKA_OBJECT_ID (0x12UL)
+#define CKA_CERTIFICATE_TYPE (0x80UL)
+#define CKA_ISSUER (0x81UL)
+#define CKA_SERIAL_NUMBER (0x82UL)
+#define CKA_AC_ISSUER (0x83UL)
+#define CKA_OWNER (0x84UL)
+#define CKA_ATTR_TYPES (0x85UL)
+#define CKA_TRUSTED (0x86UL)
+#define CKA_CERTIFICATE_CATEGORY (0x87UL)
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88UL)
+#define CKA_URL (0x89UL)
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8aUL)
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8bUL)
+#define CKA_CHECK_VALUE (0x90UL)
+#define CKA_KEY_TYPE (0x100UL)
+#define CKA_SUBJECT (0x101UL)
+#define CKA_ID (0x102UL)
+#define CKA_SENSITIVE (0x103UL)
+#define CKA_ENCRYPT (0x104UL)
+#define CKA_DECRYPT (0x105UL)
+#define CKA_WRAP (0x106UL)
+#define CKA_UNWRAP (0x107UL)
+#define CKA_SIGN (0x108UL)
+#define CKA_SIGN_RECOVER (0x109UL)
+#define CKA_VERIFY (0x10aUL)
+#define CKA_VERIFY_RECOVER (0x10bUL)
+#define CKA_DERIVE (0x10cUL)
+#define CKA_START_DATE (0x110UL)
+#define CKA_END_DATE (0x111UL)
+#define CKA_MODULUS (0x120UL)
+#define CKA_MODULUS_BITS (0x121UL)
+#define CKA_PUBLIC_EXPONENT (0x122UL)
+#define CKA_PRIVATE_EXPONENT (0x123UL)
+#define CKA_PRIME_1 (0x124UL)
+#define CKA_PRIME_2 (0x125UL)
+#define CKA_EXPONENT_1 (0x126UL)
+#define CKA_EXPONENT_2 (0x127UL)
+#define CKA_COEFFICIENT (0x128UL)
+#define CKA_PRIME (0x130UL)
+#define CKA_SUBPRIME (0x131UL)
+#define CKA_BASE (0x132UL)
+#define CKA_PRIME_BITS (0x133UL)
+#define CKA_SUB_PRIME_BITS (0x134UL)
+#define CKA_VALUE_BITS (0x160UL)
+#define CKA_VALUE_LEN (0x161UL)
+#define CKA_EXTRACTABLE (0x162UL)
+#define CKA_LOCAL (0x163UL)
+#define CKA_NEVER_EXTRACTABLE (0x164UL)
+#define CKA_ALWAYS_SENSITIVE (0x165UL)
+#define CKA_KEY_GEN_MECHANISM (0x166UL)
+#define CKA_MODIFIABLE (0x170UL)
+#define CKA_ECDSA_PARAMS (0x180UL)
+#define CKA_EC_PARAMS (0x180UL)
+#define CKA_EC_POINT (0x181UL)
+#define CKA_SECONDARY_AUTH (0x200UL)
+#define CKA_AUTH_PIN_FLAGS (0x201UL)
+#define CKA_ALWAYS_AUTHENTICATE (0x202UL)
+#define CKA_WRAP_WITH_TRUSTED (0x210UL)
+#define CKA_GOSTR3410_PARAMS (0x250UL)
+#define CKA_GOSTR3411_PARAMS (0x251UL)
+#define CKA_GOST28147_PARAMS (0x252UL)
+#define CKA_HW_FEATURE_TYPE (0x300UL)
+#define CKA_RESET_ON_INIT (0x301UL)
+#define CKA_HAS_RESET (0x302UL)
+#define CKA_PIXEL_X (0x400UL)
+#define CKA_PIXEL_Y (0x401UL)
+#define CKA_RESOLUTION (0x402UL)
+#define CKA_CHAR_ROWS (0x403UL)
+#define CKA_CHAR_COLUMNS (0x404UL)
+#define CKA_COLOR (0x405UL)
+#define CKA_BITS_PER_PIXEL (0x406UL)
+#define CKA_CHAR_SETS (0x480UL)
+#define CKA_ENCODING_METHODS (0x481UL)
+#define CKA_MIME_TYPES (0x482UL)
+#define CKA_MECHANISM_TYPE (0x500UL)
+#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501UL)
+#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502UL)
+#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503UL)
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211UL)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212UL)
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL)
+#define CKA_VENDOR_DEFINED (1UL << 31)
+
+
+struct ck_attribute
+{
+ ck_attribute_type_t type;
+ void *value;
+ unsigned long value_len;
+};
+
+
+struct ck_date
+{
+ unsigned char year[4];
+ unsigned char month[2];
+ unsigned char day[2];
+};
+
+
+typedef unsigned long ck_mechanism_type_t;
+
+#define CKM_RSA_PKCS_KEY_PAIR_GEN (0UL)
+#define CKM_RSA_PKCS (1UL)
+#define CKM_RSA_9796 (2UL)
+#define CKM_RSA_X_509 (3UL)
+#define CKM_MD2_RSA_PKCS (4UL)
+#define CKM_MD5_RSA_PKCS (5UL)
+#define CKM_SHA1_RSA_PKCS (6UL)
+#define CKM_RIPEMD128_RSA_PKCS (7UL)
+#define CKM_RIPEMD160_RSA_PKCS (8UL)
+#define CKM_RSA_PKCS_OAEP (9UL)
+#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xaUL)
+#define CKM_RSA_X9_31 (0xbUL)
+#define CKM_SHA1_RSA_X9_31 (0xcUL)
+#define CKM_RSA_PKCS_PSS (0xdUL)
+#define CKM_SHA1_RSA_PKCS_PSS (0xeUL)
+#define CKM_DSA_KEY_PAIR_GEN (0x10UL)
+#define CKM_DSA (0x11UL)
+#define CKM_DSA_SHA1 (0x12UL)
+#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20UL)
+#define CKM_DH_PKCS_DERIVE (0x21UL)
+#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30UL)
+#define CKM_X9_42_DH_DERIVE (0x31UL)
+#define CKM_X9_42_DH_HYBRID_DERIVE (0x32UL)
+#define CKM_X9_42_MQV_DERIVE (0x33UL)
+#define CKM_SHA256_RSA_PKCS (0x40UL)
+#define CKM_SHA384_RSA_PKCS (0x41UL)
+#define CKM_SHA512_RSA_PKCS (0x42UL)
+#define CKM_SHA256_RSA_PKCS_PSS (0x43UL)
+#define CKM_SHA384_RSA_PKCS_PSS (0x44UL)
+#define CKM_SHA512_RSA_PKCS_PSS (0x45UL)
+#define CKM_RC2_KEY_GEN (0x100UL)
+#define CKM_RC2_ECB (0x101UL)
+#define CKM_RC2_CBC (0x102UL)
+#define CKM_RC2_MAC (0x103UL)
+#define CKM_RC2_MAC_GENERAL (0x104UL)
+#define CKM_RC2_CBC_PAD (0x105UL)
+#define CKM_RC4_KEY_GEN (0x110UL)
+#define CKM_RC4 (0x111UL)
+#define CKM_DES_KEY_GEN (0x120UL)
+#define CKM_DES_ECB (0x121UL)
+#define CKM_DES_CBC (0x122UL)
+#define CKM_DES_MAC (0x123UL)
+#define CKM_DES_MAC_GENERAL (0x124UL)
+#define CKM_DES_CBC_PAD (0x125UL)
+#define CKM_DES2_KEY_GEN (0x130UL)
+#define CKM_DES3_KEY_GEN (0x131UL)
+#define CKM_DES3_ECB (0x132UL)
+#define CKM_DES3_CBC (0x133UL)
+#define CKM_DES3_MAC (0x134UL)
+#define CKM_DES3_MAC_GENERAL (0x135UL)
+#define CKM_DES3_CBC_PAD (0x136UL)
+#define CKM_CDMF_KEY_GEN (0x140UL)
+#define CKM_CDMF_ECB (0x141UL)
+#define CKM_CDMF_CBC (0x142UL)
+#define CKM_CDMF_MAC (0x143UL)
+#define CKM_CDMF_MAC_GENERAL (0x144UL)
+#define CKM_CDMF_CBC_PAD (0x145UL)
+#define CKM_MD2 (0x200UL)
+#define CKM_MD2_HMAC (0x201UL)
+#define CKM_MD2_HMAC_GENERAL (0x202UL)
+#define CKM_MD5 (0x210UL)
+#define CKM_MD5_HMAC (0x211UL)
+#define CKM_MD5_HMAC_GENERAL (0x212UL)
+#define CKM_SHA_1 (0x220UL)
+#define CKM_SHA_1_HMAC (0x221UL)
+#define CKM_SHA_1_HMAC_GENERAL (0x222UL)
+#define CKM_RIPEMD128 (0x230UL)
+#define CKM_RIPEMD128_HMAC (0x231UL)
+#define CKM_RIPEMD128_HMAC_GENERAL (0x232UL)
+#define CKM_RIPEMD160 (0x240UL)
+#define CKM_RIPEMD160_HMAC (0x241UL)
+#define CKM_RIPEMD160_HMAC_GENERAL (0x242UL)
+#define CKM_SHA256 (0x250UL)
+#define CKM_SHA256_HMAC (0x251UL)
+#define CKM_SHA256_HMAC_GENERAL (0x252UL)
+#define CKM_SHA384 (0x260UL)
+#define CKM_SHA384_HMAC (0x261UL)
+#define CKM_SHA384_HMAC_GENERAL (0x262UL)
+#define CKM_SHA512 (0x270UL)
+#define CKM_SHA512_HMAC (0x271UL)
+#define CKM_SHA512_HMAC_GENERAL (0x272UL)
+#define CKM_CAST_KEY_GEN (0x300UL)
+#define CKM_CAST_ECB (0x301UL)
+#define CKM_CAST_CBC (0x302UL)
+#define CKM_CAST_MAC (0x303UL)
+#define CKM_CAST_MAC_GENERAL (0x304UL)
+#define CKM_CAST_CBC_PAD (0x305UL)
+#define CKM_CAST3_KEY_GEN (0x310UL)
+#define CKM_CAST3_ECB (0x311UL)
+#define CKM_CAST3_CBC (0x312UL)
+#define CKM_CAST3_MAC (0x313UL)
+#define CKM_CAST3_MAC_GENERAL (0x314UL)
+#define CKM_CAST3_CBC_PAD (0x315UL)
+#define CKM_CAST5_KEY_GEN (0x320UL)
+#define CKM_CAST128_KEY_GEN (0x320UL)
+#define CKM_CAST5_ECB (0x321UL)
+#define CKM_CAST128_ECB (0x321UL)
+#define CKM_CAST5_CBC (0x322UL)
+#define CKM_CAST128_CBC (0x322UL)
+#define CKM_CAST5_MAC (0x323UL)
+#define CKM_CAST128_MAC (0x323UL)
+#define CKM_CAST5_MAC_GENERAL (0x324UL)
+#define CKM_CAST128_MAC_GENERAL (0x324UL)
+#define CKM_CAST5_CBC_PAD (0x325UL)
+#define CKM_CAST128_CBC_PAD (0x325UL)
+#define CKM_RC5_KEY_GEN (0x330UL)
+#define CKM_RC5_ECB (0x331UL)
+#define CKM_RC5_CBC (0x332UL)
+#define CKM_RC5_MAC (0x333UL)
+#define CKM_RC5_MAC_GENERAL (0x334UL)
+#define CKM_RC5_CBC_PAD (0x335UL)
+#define CKM_IDEA_KEY_GEN (0x340UL)
+#define CKM_IDEA_ECB (0x341UL)
+#define CKM_IDEA_CBC (0x342UL)
+#define CKM_IDEA_MAC (0x343UL)
+#define CKM_IDEA_MAC_GENERAL (0x344UL)
+#define CKM_IDEA_CBC_PAD (0x345UL)
+#define CKM_GENERIC_SECRET_KEY_GEN (0x350UL)
+#define CKM_CONCATENATE_BASE_AND_KEY (0x360UL)
+#define CKM_CONCATENATE_BASE_AND_DATA (0x362UL)
+#define CKM_CONCATENATE_DATA_AND_BASE (0x363UL)
+#define CKM_XOR_BASE_AND_DATA (0x364UL)
+#define CKM_EXTRACT_KEY_FROM_KEY (0x365UL)
+#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370UL)
+#define CKM_SSL3_MASTER_KEY_DERIVE (0x371UL)
+#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372UL)
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373UL)
+#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374UL)
+#define CKM_TLS_MASTER_KEY_DERIVE (0x375UL)
+#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376UL)
+#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377UL)
+#define CKM_SSL3_MD5_MAC (0x380UL)
+#define CKM_SSL3_SHA1_MAC (0x381UL)
+#define CKM_MD5_KEY_DERIVATION (0x390UL)
+#define CKM_MD2_KEY_DERIVATION (0x391UL)
+#define CKM_SHA1_KEY_DERIVATION (0x392UL)
+#define CKM_PBE_MD2_DES_CBC (0x3a0UL)
+#define CKM_PBE_MD5_DES_CBC (0x3a1UL)
+#define CKM_PBE_MD5_CAST_CBC (0x3a2UL)
+#define CKM_PBE_MD5_CAST3_CBC (0x3a3UL)
+#define CKM_PBE_MD5_CAST5_CBC (0x3a4UL)
+#define CKM_PBE_MD5_CAST128_CBC (0x3a4UL)
+#define CKM_PBE_SHA1_CAST5_CBC (0x3a5UL)
+#define CKM_PBE_SHA1_CAST128_CBC (0x3a5UL)
+#define CKM_PBE_SHA1_RC4_128 (0x3a6UL)
+#define CKM_PBE_SHA1_RC4_40 (0x3a7UL)
+#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8UL)
+#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9UL)
+#define CKM_PBE_SHA1_RC2_128_CBC (0x3aaUL)
+#define CKM_PBE_SHA1_RC2_40_CBC (0x3abUL)
+#define CKM_PKCS5_PBKD2 (0x3b0UL)
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0UL)
+#define CKM_KEY_WRAP_LYNKS (0x400UL)
+#define CKM_KEY_WRAP_SET_OAEP (0x401UL)
+#define CKM_SKIPJACK_KEY_GEN (0x1000UL)
+#define CKM_SKIPJACK_ECB64 (0x1001UL)
+#define CKM_SKIPJACK_CBC64 (0x1002UL)
+#define CKM_SKIPJACK_OFB64 (0x1003UL)
+#define CKM_SKIPJACK_CFB64 (0x1004UL)
+#define CKM_SKIPJACK_CFB32 (0x1005UL)
+#define CKM_SKIPJACK_CFB16 (0x1006UL)
+#define CKM_SKIPJACK_CFB8 (0x1007UL)
+#define CKM_SKIPJACK_WRAP (0x1008UL)
+#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009UL)
+#define CKM_SKIPJACK_RELAYX (0x100aUL)
+#define CKM_KEA_KEY_PAIR_GEN (0x1010UL)
+#define CKM_KEA_KEY_DERIVE (0x1011UL)
+#define CKM_FORTEZZA_TIMESTAMP (0x1020UL)
+#define CKM_BATON_KEY_GEN (0x1030UL)
+#define CKM_BATON_ECB128 (0x1031UL)
+#define CKM_BATON_ECB96 (0x1032UL)
+#define CKM_BATON_CBC128 (0x1033UL)
+#define CKM_BATON_COUNTER (0x1034UL)
+#define CKM_BATON_SHUFFLE (0x1035UL)
+#define CKM_BATON_WRAP (0x1036UL)
+#define CKM_ECDSA_KEY_PAIR_GEN (0x1040UL)
+#define CKM_EC_KEY_PAIR_GEN (0x1040UL)
+#define CKM_ECDSA (0x1041UL)
+#define CKM_ECDSA_SHA1 (0x1042UL)
+#define CKM_ECDSA_SHA256 (0x1044UL)
+#define CKM_ECDSA_SHA384 (0x1045UL)
+#define CKM_ECDSA_SHA512 (0x1046UL)
+#define CKM_ECDH1_DERIVE (0x1050UL)
+#define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL)
+#define CKM_ECMQV_DERIVE (0x1052UL)
+#define CKM_EC_EDWARDS_KEY_PAIR_GEN (0x1055UL)
+#define CKM_EC_MONTGOMERY_KEY_PAIR_GEN (0x1056UL)
+#define CKM_EDDSA (0x1057UL)
+#define CKM_JUNIPER_KEY_GEN (0x1060UL)
+#define CKM_JUNIPER_ECB128 (0x1061UL)
+#define CKM_JUNIPER_CBC128 (0x1062UL)
+#define CKM_JUNIPER_COUNTER (0x1063UL)
+#define CKM_JUNIPER_SHUFFLE (0x1064UL)
+#define CKM_JUNIPER_WRAP (0x1065UL)
+#define CKM_FASTHASH (0x1070UL)
+#define CKM_AES_KEY_GEN (0x1080UL)
+#define CKM_AES_ECB (0x1081UL)
+#define CKM_AES_CBC (0x1082UL)
+#define CKM_AES_MAC (0x1083UL)
+#define CKM_AES_MAC_GENERAL (0x1084UL)
+#define CKM_AES_CBC_PAD (0x1085UL)
+#define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL)
+#define CKM_GOSTR3410 (0x1201UL)
+#define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL)
+#define CKM_GOSTR3411 (0x1210UL)
+#define CKM_DSA_PARAMETER_GEN (0x2000UL)
+#define CKM_DH_PKCS_PARAMETER_GEN (0x2001UL)
+#define CKM_X9_42_DH_PARAMETER_GEN (0x2002UL)
+#define CKM_VENDOR_DEFINED (1UL << 31)
+
+
+struct ck_mechanism
+{
+ ck_mechanism_type_t mechanism;
+ void *parameter;
+ unsigned long parameter_len;
+};
+
+
+struct ck_mechanism_info
+{
+ unsigned long min_key_size;
+ unsigned long max_key_size;
+ ck_flags_t flags;
+};
+
+#define CKF_HW (1UL << 0)
+#define CKF_ENCRYPT (1UL << 8)
+#define CKF_DECRYPT (1UL << 9)
+#define CKF_DIGEST (1UL << 10)
+#define CKF_SIGN (1UL << 11)
+#define CKF_SIGN_RECOVER (1UL << 12)
+#define CKF_VERIFY (1UL << 13)
+#define CKF_VERIFY_RECOVER (1UL << 14)
+#define CKF_GENERATE (1UL << 15)
+#define CKF_GENERATE_KEY_PAIR (1UL << 16)
+#define CKF_WRAP (1UL << 17)
+#define CKF_UNWRAP (1UL << 18)
+#define CKF_DERIVE (1UL << 19)
+#define CKF_EC_F_P (1UL << 20)
+#define CKF_EC_F_2M (1UL << 21)
+#define CKF_EC_PARAMETERS (1UL << 22)
+#define CKF_EC_OID (1UL << 23)
+#define CKF_EC_UNCOMPRESS (1UL << 24)
+#define CKF_EC_COMPRESS (1UL << 25)
+#define CKF_EC_CURVENAME (1UL << 26)
+#define CKF_EXTENSION (1UL << 31)
+
+
+/* Flags for C_WaitForSlotEvent. */
+#define CKF_DONT_BLOCK (1UL)
+
+
+typedef unsigned long ck_rv_t;
+
+
+typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
+ ck_notification_t event, void *application);
+
+/* Forward reference. */
+struct ck_function_list;
+
+#define _CK_DECLARE_FUNCTION(name, args) \
+typedef ck_rv_t (*CK_ ## name) args; \
+ck_rv_t CK_SPEC name args
+
+_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
+_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
+_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
+_CK_DECLARE_FUNCTION (C_GetFunctionList,
+ (struct ck_function_list **function_list));
+
+_CK_DECLARE_FUNCTION (C_GetSlotList,
+ (unsigned char token_present, ck_slot_id_t *slot_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetSlotInfo,
+ (ck_slot_id_t slot_id, struct ck_slot_info *info));
+_CK_DECLARE_FUNCTION (C_GetTokenInfo,
+ (ck_slot_id_t slot_id, struct ck_token_info *info));
+_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
+ (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
+_CK_DECLARE_FUNCTION (C_GetMechanismList,
+ (ck_slot_id_t slot_id,
+ ck_mechanism_type_t *mechanism_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
+ (ck_slot_id_t slot_id, ck_mechanism_type_t type,
+ struct ck_mechanism_info *info));
+_CK_DECLARE_FUNCTION (C_InitToken,
+ (ck_slot_id_t slot_id, unsigned char *pin,
+ unsigned long pin_len, unsigned char *label));
+_CK_DECLARE_FUNCTION (C_InitPIN,
+ (ck_session_handle_t session, unsigned char *pin,
+ unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_SetPIN,
+ (ck_session_handle_t session, unsigned char *old_pin,
+ unsigned long old_len, unsigned char *new_pin,
+ unsigned long new_len));
+
+_CK_DECLARE_FUNCTION (C_OpenSession,
+ (ck_slot_id_t slot_id, ck_flags_t flags,
+ void *application, ck_notify_t notify,
+ ck_session_handle_t *session));
+_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
+_CK_DECLARE_FUNCTION (C_GetSessionInfo,
+ (ck_session_handle_t session,
+ struct ck_session_info *info));
+_CK_DECLARE_FUNCTION (C_GetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long *operation_state_len));
+_CK_DECLARE_FUNCTION (C_SetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long operation_state_len,
+ ck_object_handle_t encryption_key,
+ ck_object_handle_t authentiation_key));
+_CK_DECLARE_FUNCTION (C_Login,
+ (ck_session_handle_t session, ck_user_type_t user_type,
+ unsigned char *pin, unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_CreateObject,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count, ck_object_handle_t *object));
+_CK_DECLARE_FUNCTION (C_CopyObject,
+ (ck_session_handle_t session, ck_object_handle_t object,
+ struct ck_attribute *templ, unsigned long count,
+ ck_object_handle_t *new_object));
+_CK_DECLARE_FUNCTION (C_DestroyObject,
+ (ck_session_handle_t session,
+ ck_object_handle_t object));
+_CK_DECLARE_FUNCTION (C_GetObjectSize,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ unsigned long *size));
+_CK_DECLARE_FUNCTION (C_GetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_SetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjectsInit,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjects,
+ (ck_session_handle_t session,
+ ck_object_handle_t *object,
+ unsigned long max_object_count,
+ unsigned long *object_count));
+_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
+ (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_EncryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Encrypt,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *encrypted_data,
+ unsigned long *encrypted_data_len));
+_CK_DECLARE_FUNCTION (C_EncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_EncryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_encrypted_part,
+ unsigned long *last_encrypted_part_len));
+
+_CK_DECLARE_FUNCTION (C_DecryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Decrypt,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_data,
+ unsigned long encrypted_data_len,
+ unsigned char *data, unsigned long *data_len));
+_CK_DECLARE_FUNCTION (C_DecryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part, unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_DecryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_part,
+ unsigned long *last_part_len));
+
+_CK_DECLARE_FUNCTION (C_DigestInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism));
+_CK_DECLARE_FUNCTION (C_Digest,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *digest,
+ unsigned long *digest_len));
+_CK_DECLARE_FUNCTION (C_DigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_DigestKey,
+ (ck_session_handle_t session, ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_DigestFinal,
+ (ck_session_handle_t session,
+ unsigned char *digest,
+ unsigned long *digest_len));
+
+_CK_DECLARE_FUNCTION (C_SignInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Sign,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_SignFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_SignRecover,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+
+_CK_DECLARE_FUNCTION (C_VerifyInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Verify,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_VerifyFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_VerifyRecover,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len,
+ unsigned char *data,
+ unsigned long *data_len));
+
+_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+
+_CK_DECLARE_FUNCTION (C_GenerateKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *templ,
+ unsigned long count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *public_key_template,
+ unsigned long public_key_attribute_count,
+ struct ck_attribute *private_key_template,
+ unsigned long private_key_attribute_count,
+ ck_object_handle_t *public_key,
+ ck_object_handle_t *private_key));
+_CK_DECLARE_FUNCTION (C_WrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t wrapping_key,
+ ck_object_handle_t key,
+ unsigned char *wrapped_key,
+ unsigned long *wrapped_key_len));
+_CK_DECLARE_FUNCTION (C_UnwrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t unwrapping_key,
+ unsigned char *wrapped_key,
+ unsigned long wrapped_key_len,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_DeriveKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t base_key,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+
+_CK_DECLARE_FUNCTION (C_SeedRandom,
+ (ck_session_handle_t session, unsigned char *seed,
+ unsigned long seed_len));
+_CK_DECLARE_FUNCTION (C_GenerateRandom,
+ (ck_session_handle_t session,
+ unsigned char *random_data,
+ unsigned long random_len));
+
+_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
+
+
+struct ck_function_list
+{
+ struct ck_version version;
+ CK_C_Initialize C_Initialize;
+ CK_C_Finalize C_Finalize;
+ CK_C_GetInfo C_GetInfo;
+ CK_C_GetFunctionList C_GetFunctionList;
+ CK_C_GetSlotList C_GetSlotList;
+ CK_C_GetSlotInfo C_GetSlotInfo;
+ CK_C_GetTokenInfo C_GetTokenInfo;
+ CK_C_GetMechanismList C_GetMechanismList;
+ CK_C_GetMechanismInfo C_GetMechanismInfo;
+ CK_C_InitToken C_InitToken;
+ CK_C_InitPIN C_InitPIN;
+ CK_C_SetPIN C_SetPIN;
+ CK_C_OpenSession C_OpenSession;
+ CK_C_CloseSession C_CloseSession;
+ CK_C_CloseAllSessions C_CloseAllSessions;
+ CK_C_GetSessionInfo C_GetSessionInfo;
+ CK_C_GetOperationState C_GetOperationState;
+ CK_C_SetOperationState C_SetOperationState;
+ CK_C_Login C_Login;
+ CK_C_Logout C_Logout;
+ CK_C_CreateObject C_CreateObject;
+ CK_C_CopyObject C_CopyObject;
+ CK_C_DestroyObject C_DestroyObject;
+ CK_C_GetObjectSize C_GetObjectSize;
+ CK_C_GetAttributeValue C_GetAttributeValue;
+ CK_C_SetAttributeValue C_SetAttributeValue;
+ CK_C_FindObjectsInit C_FindObjectsInit;
+ CK_C_FindObjects C_FindObjects;
+ CK_C_FindObjectsFinal C_FindObjectsFinal;
+ CK_C_EncryptInit C_EncryptInit;
+ CK_C_Encrypt C_Encrypt;
+ CK_C_EncryptUpdate C_EncryptUpdate;
+ CK_C_EncryptFinal C_EncryptFinal;
+ CK_C_DecryptInit C_DecryptInit;
+ CK_C_Decrypt C_Decrypt;
+ CK_C_DecryptUpdate C_DecryptUpdate;
+ CK_C_DecryptFinal C_DecryptFinal;
+ CK_C_DigestInit C_DigestInit;
+ CK_C_Digest C_Digest;
+ CK_C_DigestUpdate C_DigestUpdate;
+ CK_C_DigestKey C_DigestKey;
+ CK_C_DigestFinal C_DigestFinal;
+ CK_C_SignInit C_SignInit;
+ CK_C_Sign C_Sign;
+ CK_C_SignUpdate C_SignUpdate;
+ CK_C_SignFinal C_SignFinal;
+ CK_C_SignRecoverInit C_SignRecoverInit;
+ CK_C_SignRecover C_SignRecover;
+ CK_C_VerifyInit C_VerifyInit;
+ CK_C_Verify C_Verify;
+ CK_C_VerifyUpdate C_VerifyUpdate;
+ CK_C_VerifyFinal C_VerifyFinal;
+ CK_C_VerifyRecoverInit C_VerifyRecoverInit;
+ CK_C_VerifyRecover C_VerifyRecover;
+ CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
+ CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
+ CK_C_SignEncryptUpdate C_SignEncryptUpdate;
+ CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
+ CK_C_GenerateKey C_GenerateKey;
+ CK_C_GenerateKeyPair C_GenerateKeyPair;
+ CK_C_WrapKey C_WrapKey;
+ CK_C_UnwrapKey C_UnwrapKey;
+ CK_C_DeriveKey C_DeriveKey;
+ CK_C_SeedRandom C_SeedRandom;
+ CK_C_GenerateRandom C_GenerateRandom;
+ CK_C_GetFunctionStatus C_GetFunctionStatus;
+ CK_C_CancelFunction C_CancelFunction;
+ CK_C_WaitForSlotEvent C_WaitForSlotEvent;
+};
+
+
+typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
+typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
+typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
+typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
+
+
+struct ck_c_initialize_args
+{
+ ck_createmutex_t create_mutex;
+ ck_destroymutex_t destroy_mutex;
+ ck_lockmutex_t lock_mutex;
+ ck_unlockmutex_t unlock_mutex;
+ ck_flags_t flags;
+ void *reserved;
+};
+
+
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1UL << 0)
+#define CKF_OS_LOCKING_OK (1UL << 1)
+
+#define CKR_OK (0UL)
+#define CKR_CANCEL (1UL)
+#define CKR_HOST_MEMORY (2UL)
+#define CKR_SLOT_ID_INVALID (3UL)
+#define CKR_GENERAL_ERROR (5UL)
+#define CKR_FUNCTION_FAILED (6UL)
+#define CKR_ARGUMENTS_BAD (7UL)
+#define CKR_NO_EVENT (8UL)
+#define CKR_NEED_TO_CREATE_THREADS (9UL)
+#define CKR_CANT_LOCK (0xaUL)
+#define CKR_ATTRIBUTE_READ_ONLY (0x10UL)
+#define CKR_ATTRIBUTE_SENSITIVE (0x11UL)
+#define CKR_ATTRIBUTE_TYPE_INVALID (0x12UL)
+#define CKR_ATTRIBUTE_VALUE_INVALID (0x13UL)
+#define CKR_DATA_INVALID (0x20UL)
+#define CKR_DATA_LEN_RANGE (0x21UL)
+#define CKR_DEVICE_ERROR (0x30UL)
+#define CKR_DEVICE_MEMORY (0x31UL)
+#define CKR_DEVICE_REMOVED (0x32UL)
+#define CKR_ENCRYPTED_DATA_INVALID (0x40UL)
+#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41UL)
+#define CKR_FUNCTION_CANCELED (0x50UL)
+#define CKR_FUNCTION_NOT_PARALLEL (0x51UL)
+#define CKR_FUNCTION_NOT_SUPPORTED (0x54UL)
+#define CKR_KEY_HANDLE_INVALID (0x60UL)
+#define CKR_KEY_SIZE_RANGE (0x62UL)
+#define CKR_KEY_TYPE_INCONSISTENT (0x63UL)
+#define CKR_KEY_NOT_NEEDED (0x64UL)
+#define CKR_KEY_CHANGED (0x65UL)
+#define CKR_KEY_NEEDED (0x66UL)
+#define CKR_KEY_INDIGESTIBLE (0x67UL)
+#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68UL)
+#define CKR_KEY_NOT_WRAPPABLE (0x69UL)
+#define CKR_KEY_UNEXTRACTABLE (0x6aUL)
+#define CKR_MECHANISM_INVALID (0x70UL)
+#define CKR_MECHANISM_PARAM_INVALID (0x71UL)
+#define CKR_OBJECT_HANDLE_INVALID (0x82UL)
+#define CKR_OPERATION_ACTIVE (0x90UL)
+#define CKR_OPERATION_NOT_INITIALIZED (0x91UL)
+#define CKR_PIN_INCORRECT (0xa0UL)
+#define CKR_PIN_INVALID (0xa1UL)
+#define CKR_PIN_LEN_RANGE (0xa2UL)
+#define CKR_PIN_EXPIRED (0xa3UL)
+#define CKR_PIN_LOCKED (0xa4UL)
+#define CKR_SESSION_CLOSED (0xb0UL)
+#define CKR_SESSION_COUNT (0xb1UL)
+#define CKR_SESSION_HANDLE_INVALID (0xb3UL)
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4UL)
+#define CKR_SESSION_READ_ONLY (0xb5UL)
+#define CKR_SESSION_EXISTS (0xb6UL)
+#define CKR_SESSION_READ_ONLY_EXISTS (0xb7UL)
+#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8UL)
+#define CKR_SIGNATURE_INVALID (0xc0UL)
+#define CKR_SIGNATURE_LEN_RANGE (0xc1UL)
+#define CKR_TEMPLATE_INCOMPLETE (0xd0UL)
+#define CKR_TEMPLATE_INCONSISTENT (0xd1UL)
+#define CKR_TOKEN_NOT_PRESENT (0xe0UL)
+#define CKR_TOKEN_NOT_RECOGNIZED (0xe1UL)
+#define CKR_TOKEN_WRITE_PROTECTED (0xe2UL)
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0UL)
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1UL)
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2UL)
+#define CKR_USER_ALREADY_LOGGED_IN (0x100UL)
+#define CKR_USER_NOT_LOGGED_IN (0x101UL)
+#define CKR_USER_PIN_NOT_INITIALIZED (0x102UL)
+#define CKR_USER_TYPE_INVALID (0x103UL)
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104UL)
+#define CKR_USER_TOO_MANY_TYPES (0x105UL)
+#define CKR_WRAPPED_KEY_INVALID (0x110UL)
+#define CKR_WRAPPED_KEY_LEN_RANGE (0x112UL)
+#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113UL)
+#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114UL)
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115UL)
+#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120UL)
+#define CKR_RANDOM_NO_RNG (0x121UL)
+#define CKR_DOMAIN_PARAMS_INVALID (0x130UL)
+#define CKR_BUFFER_TOO_SMALL (0x150UL)
+#define CKR_SAVED_STATE_INVALID (0x160UL)
+#define CKR_INFORMATION_SENSITIVE (0x170UL)
+#define CKR_STATE_UNSAVEABLE (0x180UL)
+#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190UL)
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191UL)
+#define CKR_MUTEX_BAD (0x1a0UL)
+#define CKR_MUTEX_NOT_LOCKED (0x1a1UL)
+#define CKR_FUNCTION_REJECTED (0x200UL)
+#define CKR_VENDOR_DEFINED (1UL << 31)
+
+
+
+/* Compatibility layer. */
+
+#ifdef CRYPTOKI_COMPAT
+
+#undef CK_DEFINE_FUNCTION
+#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
+
+/* For NULL. */
+#include <stddef.h>
+
+typedef unsigned char CK_BYTE;
+typedef unsigned char CK_CHAR;
+typedef unsigned char CK_UTF8CHAR;
+typedef unsigned char CK_BBOOL;
+typedef unsigned long int CK_ULONG;
+typedef long int CK_LONG;
+typedef CK_BYTE *CK_BYTE_PTR;
+typedef CK_CHAR *CK_CHAR_PTR;
+typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
+typedef CK_ULONG *CK_ULONG_PTR;
+typedef void *CK_VOID_PTR;
+typedef void **CK_VOID_PTR_PTR;
+#define CK_FALSE 0
+#define CK_TRUE 1
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+typedef struct ck_version CK_VERSION;
+typedef struct ck_version *CK_VERSION_PTR;
+
+typedef struct ck_info CK_INFO;
+typedef struct ck_info *CK_INFO_PTR;
+
+typedef ck_slot_id_t *CK_SLOT_ID_PTR;
+
+typedef struct ck_slot_info CK_SLOT_INFO;
+typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
+
+typedef struct ck_token_info CK_TOKEN_INFO;
+typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
+
+typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
+
+typedef struct ck_session_info CK_SESSION_INFO;
+typedef struct ck_session_info *CK_SESSION_INFO_PTR;
+
+typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
+
+typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
+
+typedef struct ck_attribute CK_ATTRIBUTE;
+typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
+
+typedef struct ck_date CK_DATE;
+typedef struct ck_date *CK_DATE_PTR;
+
+typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
+
+typedef struct ck_mechanism CK_MECHANISM;
+typedef struct ck_mechanism *CK_MECHANISM_PTR;
+
+typedef struct ck_mechanism_info CK_MECHANISM_INFO;
+typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+
+typedef struct ck_function_list CK_FUNCTION_LIST;
+typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
+typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
+
+typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
+typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+
+#define NULL_PTR NULL
+
+/* Delete the helper macros defined at the top of the file. */
+#undef ck_flags_t
+#undef ck_version
+
+#undef ck_info
+#undef cryptoki_version
+#undef manufacturer_id
+#undef library_description
+#undef library_version
+
+#undef ck_notification_t
+#undef ck_slot_id_t
+
+#undef ck_slot_info
+#undef slot_description
+#undef hardware_version
+#undef firmware_version
+
+#undef ck_token_info
+#undef serial_number
+#undef max_session_count
+#undef session_count
+#undef max_rw_session_count
+#undef rw_session_count
+#undef max_pin_len
+#undef min_pin_len
+#undef total_public_memory
+#undef free_public_memory
+#undef total_private_memory
+#undef free_private_memory
+#undef utc_time
+
+#undef ck_session_handle_t
+#undef ck_user_type_t
+#undef ck_state_t
+
+#undef ck_session_info
+#undef slot_id
+#undef device_error
+
+#undef ck_object_handle_t
+#undef ck_object_class_t
+#undef ck_hw_feature_type_t
+#undef ck_key_type_t
+#undef ck_certificate_type_t
+#undef ck_attribute_type_t
+
+#undef ck_attribute
+#undef value
+#undef value_len
+
+#undef ck_date
+
+#undef ck_mechanism_type_t
+
+#undef ck_mechanism
+#undef parameter
+#undef parameter_len
+
+#undef ck_mechanism_info
+#undef min_key_size
+#undef max_key_size
+
+#undef ck_rv_t
+#undef ck_notify_t
+
+#undef ck_function_list
+
+#undef ck_createmutex_t
+#undef ck_destroymutex_t
+#undef ck_lockmutex_t
+#undef ck_unlockmutex_t
+
+#undef ck_c_initialize_args
+#undef create_mutex
+#undef destroy_mutex
+#undef lock_mutex
+#undef unlock_mutex
+#undef reserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+/* System dependencies. */
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+#pragma pack(pop, cryptoki)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKCS11_H */
diff --git a/tkd/tkdaemon.c b/tkd/tkdaemon.c
new file mode 100644
index 000000000..c5ddd13a2
--- /dev/null
+++ b/tkd/tkdaemon.c
@@ -0,0 +1,1293 @@
+/* TKdaemon.c - The GnuPG TK Daemon
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2020 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2019 Werner Koch
+ * Copyright (C) 2020 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /*HAVE_W32_SYSTEM*/
+#include <unistd.h>
+#include <signal.h>
+#include <npth.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#define GNUPG_COMMON_NEED_AFLOCAL
+#include "tkdaemon.h"
+
+#include <gcrypt.h>
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/asshelp.h"
+#include "../common/exechelp.h"
+#include "../common/comopt.h"
+#include "../common/init.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
+enum cmd_and_opt_values
+{ aNull = 0,
+ oCsh = 'c',
+ oQuiet = 'q',
+ oSh = 's',
+ oVerbose = 'v',
+
+ oNoVerbose = 500,
+ aGPGConfList,
+ aGPGConfTest,
+ oOptions,
+ oDebug,
+ oDebugAll,
+ oDebugLevel,
+ oDebugWait,
+ oDebugAllowCoreDump,
+ oDebugLogTid,
+ oDebugAssuanLogCats,
+ oNoGreeting,
+ oNoOptions,
+ oHomedir,
+ oNoDetach,
+ oNoGrab,
+ oLogFile,
+ oServer,
+ oMultiServer,
+ oDaemon,
+ oBatch,
+ oPkcs11Driver,
+ oListenBacklog
+};
+
+
+
+static gpgrt_opt_t opts[] = {
+ ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+ ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+ ARGPARSE_header (NULL, N_("Options used for startup")),
+
+ ARGPARSE_s_n (oServer,"server", N_("run in server mode (foreground)")),
+ ARGPARSE_s_n (oMultiServer, "multi-server",
+ N_("run in multi server mode (foreground)")),
+ ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
+ ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
+ ARGPARSE_s_s (oHomedir, "homedir", "@"),
+ ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")),
+ ARGPARSE_noconffile (oNoOptions, "no-options", "@"),
+
+
+ ARGPARSE_header ("Monitor", N_("Options controlling the diagnostic output")),
+
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+ ARGPARSE_s_s (oDebug, "debug", "@"),
+ ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+ ARGPARSE_s_s (oDebugLevel, "debug-level" ,
+ N_("|LEVEL|set the debugging level to LEVEL")),
+ ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+ ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+ ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
+ ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
+ ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")),
+
+
+ ARGPARSE_header ("Configuration",
+ N_("Options controlling the configuration")),
+
+ ARGPARSE_s_s (oPkcs11Driver, "pkcs11-driver",
+ N_("|NAME|use NAME as PKCS#11 driver")),
+
+ ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
+
+
+ ARGPARSE_header("Security", N_("Options controlling the security")),
+
+ ARGPARSE_end ()
+};
+
+
+/* The list of supported debug flags. */
+static struct debug_flags_s debug_flags [] =
+ {
+ { DBG_MPI_VALUE , "mpi" },
+ { DBG_CRYPTO_VALUE , "crypto" },
+ { DBG_MEMORY_VALUE , "memory" },
+ { DBG_CACHE_VALUE , "cache" },
+ { DBG_MEMSTAT_VALUE, "memstat" },
+ { DBG_HASHING_VALUE, "hashing" },
+ { DBG_IPC_VALUE , "ipc" },
+ { DBG_TOKEN_VALUE , "token" },
+ { DBG_TOKEN_IO_VALUE, "tokenio" },
+ { 0, NULL }
+ };
+
+
+/* Flag to indicate that a shutdown was requested. */
+static int shutdown_pending;
+
+/* It is possible that we are currently running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Flag telling whether we are running as a pipe server. */
+static int pipe_server;
+
+/* Name of the communication socket */
+static char *socket_name;
+/* Name of the redirected socket or NULL. */
+static char *redir_socket_name;
+
+/* We need to keep track of the server's nonces (these are dummies for
+ POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+
+/* Value for the listen() backlog argument. Change at runtime with
+ * --listen-backlog. */
+static int listen_backlog = 64;
+
+#ifdef HAVE_W32_SYSTEM
+static HANDLE the_event;
+#else
+/* PID to notify update of usb devices. */
+static pid_t main_thread_pid;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+/* FD to notify changes. */
+static int notify_fd;
+#endif
+
+static char *create_socket_name (char *standard_name);
+static gnupg_fd_t create_server_socket (const char *name,
+ char **r_redir_name,
+ assuan_sock_nonce_t *nonce);
+
+static void *start_connection_thread (void *arg);
+static void handle_connections (gnupg_fd_t listen_fd);
+
+/* Pth wrapper function definitions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+static int active_connections;
+
+
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+ const char *s;
+ char *result;
+
+ if (maybe_setuid)
+ {
+ gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
+ maybe_setuid = 0;
+ }
+ s = getfnc (NULL);
+ result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
+ strcpy (stpcpy (stpcpy (result, libname), " "), s);
+ return result;
+}
+
+
+static const char *
+my_strusage (int level)
+{
+ static char *ver_gcry;
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "GPL-3.0-or-later"; break;
+ case 11: p = "@TKDAEMON@ (@GNUPG@)";
+ break;
+ case 13: p = VERSION; break;
+ case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+ case 20:
+ if (!ver_gcry)
+ ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+ p = ver_gcry;
+ break;
+ case 1:
+ case 40: p = _("Usage: @TKDAEMON@ [options] (-h for help)");
+ break;
+ case 41: p = _("Syntax: tkdaemon [options] [command [args]]\n"
+ "Token daemon for @GNUPG@\n");
+ break;
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+
+static int
+tid_log_callback (unsigned long *rvalue)
+{
+ int len = sizeof (*rvalue);
+ npth_t thread;
+
+ thread = npth_self ();
+ if (sizeof (thread) < len)
+ len = sizeof (thread);
+ memcpy (rvalue, &thread, len);
+
+ return 2; /* Use use hex representation. */
+}
+
+
+/* Setup the debugging. With a LEVEL of NULL only the active debug
+ flags are propagated to the subsystems. With LEVEL set, a specific
+ set of debug flags is set; thus overriding all flags already
+ set. */
+static void
+set_debug (const char *level)
+{
+ int numok = (level && digitp (level));
+ int numlvl = numok? atoi (level) : 0;
+
+ if (!level)
+ ;
+ else if (!strcmp (level, "none") || (numok && numlvl < 1))
+ opt.debug = 0;
+ else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
+ opt.debug = DBG_IPC_VALUE;
+ else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
+ opt.debug = DBG_IPC_VALUE;
+ else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
+ opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_TOKEN_IO_VALUE);
+ else if (!strcmp (level, "guru") || numok)
+ {
+ opt.debug = ~0;
+ /* Unless the "guru" string has been used we don't want to allow
+ hashing debugging. The rationale is that people tend to
+ select the highest debug value and would then clutter their
+ disk with debug files which may reveal confidential data. */
+ if (numok)
+ opt.debug &= ~(DBG_HASHING_VALUE);
+ }
+ else
+ {
+ log_error (_("invalid debug-level '%s' given\n"), level);
+ tkd_exit(2);
+ }
+
+
+ if (opt.debug && !opt.verbose)
+ opt.verbose = 1;
+ if (opt.debug && opt.quiet)
+ opt.quiet = 0;
+
+ if (opt.debug & DBG_MPI_VALUE)
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+ if (opt.debug & DBG_CRYPTO_VALUE )
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+ if (opt.debug)
+ parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+
+static void
+cleanup (void)
+{
+ if (socket_name && *socket_name)
+ {
+ char *name;
+
+ name = redir_socket_name? redir_socket_name : socket_name;
+
+ gnupg_remove (name);
+ *socket_name = 0;
+ }
+}
+
+static void
+setup_signal_mask (void)
+{
+#ifndef HAVE_W32_SYSTEM
+ npth_sigev_init ();
+ npth_sigev_add (SIGHUP);
+ npth_sigev_add (SIGUSR1);
+ npth_sigev_add (SIGUSR2);
+ npth_sigev_add (SIGINT);
+ npth_sigev_add (SIGCONT);
+ npth_sigev_add (SIGTERM);
+ npth_sigev_fini ();
+ main_thread_pid = getpid ();
+#endif
+}
+
+
+int
+main (int argc, char **argv )
+{
+ gpgrt_argparse_t pargs;
+ int orig_argc;
+ char **orig_argv;
+ char *last_configname = NULL;
+ const char *configname = NULL;
+ int debug_argparser = 0;
+ const char *debug_level = NULL;
+ int greeting = 0;
+ int nogreeting = 0;
+ int multi_server = 0;
+ int is_daemon = 0;
+ int nodetach = 0;
+ char *logfile = NULL;
+ int debug_wait = 0;
+ int gpgconf_list = 0;
+ char *config_filename = NULL;
+ int allow_coredump = 0;
+ struct assuan_malloc_hooks malloc_hooks;
+ int res;
+ npth_t pipecon_handler;
+
+ early_system_init ();
+ gpgrt_set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ /* Please note that we may running SUID(ROOT), so be very CAREFUL
+ when adding any stuff between here and the call to INIT_SECMEM()
+ somewhere after the option parsing */
+ log_set_prefix ("tkdaemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
+
+ /* Make sure that our subsystems are ready. */
+ i18n_init ();
+ init_common_subsystems (&argc, &argv);
+
+ malloc_hooks.malloc = gcry_malloc;
+ malloc_hooks.realloc = gcry_realloc;
+ malloc_hooks.free = gcry_free;
+ assuan_set_malloc_hooks (&malloc_hooks);
+ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+ assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+ assuan_sock_init ();
+ setup_libassuan_logging (&opt.debug, NULL);
+
+ setup_libgcrypt_logging ();
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ disable_core_dumps ();
+
+ /* Set default options. */
+
+ /* Check whether we have a config file on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+ while (gpgrt_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oDebug:
+ case oDebugAll:
+ debug_argparser++;
+ break;
+ case oHomedir:
+ gnupg_set_homedir (pargs.r.ret_str);
+ break;
+ }
+ }
+ /* Reset the flags. */
+ pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+
+ /* initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ Now we are working under our real uid
+ */
+
+ /* The configuraton directories for use by gpgrt_argparser. */
+ gpgrt_set_confdir (GPGRT_CONFDIR_SYS, gnupg_sysconfdir ());
+ gpgrt_set_confdir (GPGRT_CONFDIR_USER, gnupg_homedir ());
+
+ /* We are re-using the struct, thus the reset flag. We OR the
+ * flags so that the internal intialized flag won't be cleared. */
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags |= (ARGPARSE_FLAG_RESET
+ | ARGPARSE_FLAG_KEEP
+ | ARGPARSE_FLAG_SYS
+ | ARGPARSE_FLAG_USER);
+ while (gpgrt_argparser (&pargs, opts, TKDAEMON_NAME EXTSEP_S "conf"))
+ {
+ switch (pargs.r_opt)
+ {
+ case ARGPARSE_CONFFILE:
+ if (debug_argparser)
+ log_info (_("reading options from '%s'\n"),
+ pargs.r_type? pargs.r.ret_str: "[cmdline]");
+ if (pargs.r_type)
+ {
+ xfree (last_configname);
+ last_configname = xstrdup (pargs.r.ret_str);
+ configname = last_configname;
+ }
+ else
+ configname = NULL;
+ break;
+
+ case aGPGConfList: gpgconf_list = 1; break;
+ case aGPGConfTest: gpgconf_list = 2; break;
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: opt.verbose++; break;
+ case oBatch: opt.batch=1; break;
+
+ case oDebug:
+ if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oDebugLevel: debug_level = pargs.r.ret_str; break;
+ case oDebugWait: debug_wait = pargs.r.ret_int; break;
+ case oDebugAllowCoreDump:
+ enable_core_dumps ();
+ allow_coredump = 1;
+ break;
+ case oDebugLogTid:
+ log_set_pid_suffix_cb (tid_log_callback);
+ break;
+ case oDebugAssuanLogCats:
+ set_libassuan_log_cats (pargs.r.ret_ulong);
+ break;
+
+ case oNoGreeting: nogreeting = 1; break;
+ case oNoVerbose: opt.verbose = 0; break;
+ case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+ case oNoDetach: nodetach = 1; break;
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oServer: pipe_server = 1; break;
+ case oMultiServer: pipe_server = 1; multi_server = 1; break;
+ case oDaemon: is_daemon = 1; break;
+
+ case oPkcs11Driver: opt.pkcs11_driver = pargs.r.ret_str; break;
+
+ case oListenBacklog:
+ listen_backlog = pargs.r.ret_int;
+ break;
+
+ default:
+ if (configname)
+ pargs.err = ARGPARSE_PRINT_WARNING;
+ else
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ break;
+ }
+ }
+
+ gpgrt_argparse (NULL, &pargs, NULL); /* Release internal state. */
+
+ if (!last_configname)
+ config_filename = gpgrt_fnameconcat (gnupg_homedir (),
+ TKDAEMON_NAME EXTSEP_S "conf",
+ NULL);
+ else
+ {
+ config_filename = last_configname;
+ last_configname = NULL;
+ }
+
+ if (log_get_errorcount(0))
+ exit(2);
+
+ /* Process common component options. */
+ if (parse_comopt (GNUPG_MODULE_NAME_TKDAEMON, debug_argparser))
+ exit(2);
+
+ if (!logfile)
+ {
+ logfile = comopt.logfile;
+ comopt.logfile = NULL;
+ }
+
+ if (nogreeting )
+ greeting = 0;
+
+ if (greeting)
+ {
+ es_fprintf (es_stderr, "%s %s; %s\n",
+ gpgrt_strusage (11),gpgrt_strusage (13),gpgrt_strusage (14));
+ es_fprintf (es_stderr, "%s\n", gpgrt_strusage (15));
+ }
+#ifdef IS_DEVELOPMENT_VERSION
+ log_info ("NOTE: this is a development version!\n");
+#endif
+
+ /* Print a warning if an argument looks like an option. */
+ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+ {
+ int i;
+
+ for (i=0; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == '-')
+ log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+ }
+
+ if (atexit (cleanup))
+ {
+ log_error ("atexit failed\n");
+ cleanup ();
+ exit (1);
+ }
+
+ set_debug (debug_level);
+
+ if (initialize_module_command ())
+ {
+ log_error ("initialization failed\n");
+ cleanup ();
+ exit (1);
+ }
+
+ if (gpgconf_list == 2)
+ tkd_exit (0);
+ if (gpgconf_list)
+ {
+ /* List options and default values in the GPG Conf format. */
+ es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
+
+ tkd_exit (0);
+ }
+
+ /* Now start with logging to a file if this is desired. */
+ if (logfile)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+ | GPGRT_LOG_WITH_TIME
+ | GPGRT_LOG_WITH_PID));
+ }
+
+ if (debug_wait && pipe_server)
+ {
+ log_debug ("waiting for debugger - my pid is %u .....\n",
+ (unsigned int)getpid());
+ gnupg_sleep (debug_wait);
+ log_debug ("... okay\n");
+ }
+
+ if (pipe_server)
+ {
+ /* This is the simple pipe based server */
+ ctrl_t ctrl;
+ npth_attr_t tattr;
+ gnupg_fd_t fd = GNUPG_INVALID_FD;
+
+#ifndef HAVE_W32_SYSTEM
+ {
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &sa, NULL);
+ }
+#endif
+
+ npth_init ();
+ setup_signal_mask ();
+ gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+ /* If --debug-allow-core-dump has been given we also need to
+ switch the working directory to a place where we can actually
+ write. */
+ if (allow_coredump)
+ {
+ if (chdir("/tmp"))
+ log_debug ("chdir to '/tmp' failed: %s\n", strerror (errno));
+ else
+ log_debug ("changed working directory to '/tmp'\n");
+ }
+
+ /* In multi server mode we need to listen on an additional
+ socket. Create that socket now before starting the handler
+ for the pipe connection. This allows that handler to send
+ back the name of that socket. */
+ if (multi_server)
+ {
+ socket_name = create_socket_name (TKDAEMON_SOCK_NAME);
+ fd = create_server_socket (socket_name,
+ &redir_socket_name, &socket_nonce);
+ }
+
+ res = npth_attr_init (&tattr);
+ if (res)
+ {
+ log_error ("error allocating thread attributes: %s\n",
+ strerror (res));
+ tkd_exit (2);
+ }
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+ ctrl = xtrycalloc (1, sizeof *ctrl);
+ if ( !ctrl )
+ {
+ log_error ("error allocating connection control data: %s\n",
+ strerror (errno) );
+ tkd_exit (2);
+ }
+ ctrl->thread_startup.fd = GNUPG_INVALID_FD;
+ res = npth_create (&pipecon_handler, &tattr, start_connection_thread, ctrl);
+ if (res)
+ {
+ log_error ("error spawning pipe connection handler: %s\n",
+ strerror (res) );
+ xfree (ctrl);
+ tkd_exit (2);
+ }
+ npth_setname_np (pipecon_handler, "pipe-connection");
+ npth_attr_destroy (&tattr);
+
+ /* We run handle_connection to wait for the shutdown signal and
+ to run the ticker stuff. */
+ handle_connections (fd);
+ if (fd != GNUPG_INVALID_FD)
+ assuan_sock_close (fd);
+ }
+ else if (!is_daemon)
+ {
+ log_info (_("please use the option '--daemon'"
+ " to run the program in the background\n"));
+ }
+ else
+ { /* Regular server mode */
+ gnupg_fd_t fd;
+#ifndef HAVE_W32_SYSTEM
+ pid_t pid;
+ int i;
+#endif
+
+ /* Create the socket. */
+ socket_name = create_socket_name (TKDAEMON_SOCK_NAME);
+ fd = create_server_socket (socket_name,
+ &redir_socket_name, &socket_nonce);
+
+
+ fflush (NULL);
+#ifdef HAVE_W32_SYSTEM
+ (void)nodetach;
+#else
+ pid = fork ();
+ if (pid == (pid_t)-1)
+ {
+ log_fatal ("fork failed: %s\n", strerror (errno) );
+ exit (1);
+ }
+ else if (pid)
+ { /* we are the parent */
+ char *infostr;
+
+ close (fd);
+
+ /* create the info string: <name>:<pid>:<protocol_version> */
+ if (gpgrt_asprintf (&infostr, "TKDAEMON_INFO=%s:%lu:1",
+ socket_name, (ulong) pid) < 0)
+ {
+ log_error ("out of core\n");
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ *socket_name = 0; /* don't let cleanup() remove the socket -
+ the child should do this from now on */
+ if (argc)
+ { /* run the program given on the commandline */
+ if (putenv (infostr))
+ {
+ log_error ("failed to set environment: %s\n",
+ strerror (errno) );
+ kill (pid, SIGTERM );
+ exit (1);
+ }
+ execvp (argv[0], argv);
+ log_error ("failed to run the command: %s\n", strerror (errno));
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ else
+ {
+ xfree (infostr);
+ exit (0);
+ }
+ /* NOTREACHED */
+ } /* end parent */
+
+ /* This is the child. */
+
+ npth_init ();
+ setup_signal_mask ();
+ gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+ /* Detach from tty and put process into a new session. */
+ if (!nodetach )
+ {
+ /* Close stdin, stdout and stderr unless it is the log stream. */
+ for (i=0; i <= 2; i++)
+ {
+ if (!log_test_fd (i) && i != fd )
+ {
+ if ( !close (i)
+ && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+ {
+ log_error ("failed to open '%s': %s\n",
+ "/dev/null", strerror (errno));
+ cleanup ();
+ exit (1);
+ }
+ }
+ }
+
+ if (setsid() == -1)
+ {
+ log_error ("setsid() failed: %s\n", strerror(errno) );
+ cleanup ();
+ exit (1);
+ }
+ }
+
+ {
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &sa, NULL);
+ }
+
+#endif /*!HAVE_W32_SYSTEM*/
+
+ if (gnupg_chdir (gnupg_daemon_rootdir ()))
+ {
+ log_error ("chdir to '%s' failed: %s\n",
+ gnupg_daemon_rootdir (), strerror (errno));
+ exit (1);
+ }
+
+ handle_connections (fd);
+
+ assuan_sock_close (fd);
+ }
+
+ xfree (config_filename);
+ return 0;
+}
+
+void
+tkd_exit (int rc)
+{
+ gcry_control (GCRYCTL_TERM_SECMEM );
+ rc = rc? rc : log_get_errorcount(0)? 2 : 0;
+ exit (rc);
+}
+
+
+static void
+tkd_init_default_ctrl (ctrl_t ctrl)
+{
+ (void)ctrl;
+}
+
+static void
+tkd_deinit_default_ctrl (ctrl_t ctrl)
+{
+ if (!ctrl)
+ return;
+ xfree (ctrl->in_data.value);
+ ctrl->in_data.value = NULL;
+ ctrl->in_data.valuelen = 0;
+}
+
+
+/* Return the name of the socket to be used to connect to this
+ process. If no socket is available, return NULL. */
+const char *
+tkd_get_socket_name (void)
+{
+ if (socket_name && *socket_name)
+ return socket_name;
+ return NULL;
+}
+
+
+#ifndef HAVE_W32_SYSTEM
+static void
+handle_signal (int signo)
+{
+ switch (signo)
+ {
+ case SIGHUP:
+ log_info ("SIGHUP received - "
+ "re-reading configuration and resetting tokens\n");
+/* reread_configuration (); */
+ break;
+
+ case SIGUSR1:
+ log_info ("SIGUSR1 received - printing internal information:\n");
+ break;
+
+ case SIGUSR2:
+ log_info ("SIGUSR2 received - no action defined\n");
+ break;
+
+ case SIGCONT:
+ /* Nothing. */
+ log_debug ("SIGCONT received - breaking select\n");
+ break;
+
+ case SIGTERM:
+ if (!shutdown_pending)
+ log_info ("SIGTERM received - shutting down ...\n");
+ else
+ log_info ("SIGTERM received - still %i running threads\n",
+ active_connections);
+ shutdown_pending++;
+ if (shutdown_pending > 2)
+ {
+ log_info ("shutdown forced\n");
+ log_info ("%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13));
+ cleanup ();
+ tkd_exit (0);
+ }
+ break;
+
+ case SIGINT:
+ log_info ("SIGINT received - immediate shutdown\n");
+ log_info( "%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13));
+ cleanup ();
+ tkd_exit (0);
+ break;
+
+ default:
+ log_info ("signal %d received - no action defined\n", signo);
+ }
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Create a name for the socket. We check for valid characters as
+ well as against a maximum allowed length for a unix domain socket
+ is done. The function terminates the process in case of an error.
+ Returns: Pointer to an allocated string with the absolute name of
+ the socket used. */
+static char *
+create_socket_name (char *standard_name)
+{
+ char *name;
+
+ name = make_filename (gnupg_socketdir (), standard_name, NULL);
+ if (strchr (name, PATHSEP_C))
+ {
+ log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
+ tkd_exit (2);
+ }
+ return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME. Returns the file descriptor
+ or terminates the process in case of an error. If the socket has
+ been redirected the name of the real socket is stored as a malloced
+ string at R_REDIR_NAME. */
+static gnupg_fd_t
+create_server_socket (const char *name, char **r_redir_name,
+ assuan_sock_nonce_t *nonce)
+{
+ struct sockaddr *addr;
+ struct sockaddr_un *unaddr;
+ socklen_t len;
+ gnupg_fd_t fd;
+ int rc;
+
+ xfree (*r_redir_name);
+ *r_redir_name = NULL;
+
+ fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+ if (fd == GNUPG_INVALID_FD)
+ {
+ log_error (_("can't create socket: %s\n"), strerror (errno));
+ tkd_exit (2);
+ }
+
+ unaddr = xmalloc (sizeof (*unaddr));
+ addr = (struct sockaddr*)unaddr;
+
+ {
+ int redirected;
+
+ if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
+ {
+ if (errno == ENAMETOOLONG)
+ log_error (_("socket name '%s' is too long\n"), name);
+ else
+ log_error ("error preparing socket '%s': %s\n",
+ name, gpg_strerror (gpg_error_from_syserror ()));
+ tkd_exit (2);
+ }
+ if (redirected)
+ {
+ *r_redir_name = xstrdup (unaddr->sun_path);
+ if (opt.verbose)
+ log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
+ }
+ }
+
+ len = SUN_LEN (unaddr);
+
+ rc = assuan_sock_bind (fd, addr, len);
+ if (rc == -1 && errno == EADDRINUSE)
+ {
+ gnupg_remove (unaddr->sun_path);
+ rc = assuan_sock_bind (fd, addr, len);
+ }
+ if (rc != -1
+ && (rc=assuan_sock_get_nonce (addr, len, nonce)))
+ log_error (_("error getting nonce for the socket\n"));
+ if (rc == -1)
+ {
+ log_error (_("error binding socket to '%s': %s\n"),
+ unaddr->sun_path,
+ gpg_strerror (gpg_error_from_syserror ()));
+ assuan_sock_close (fd);
+ tkd_exit (2);
+ }
+
+ if (gnupg_chmod (unaddr->sun_path, "-rwx"))
+ log_error (_("can't set permissions of '%s': %s\n"),
+ unaddr->sun_path, strerror (errno));
+
+ if (listen (FD2INT(fd), listen_backlog) == -1)
+ {
+ log_error ("listen(fd, %d) failed: %s\n",
+ listen_backlog, gpg_strerror (gpg_error_from_syserror ()));
+ assuan_sock_close (fd);
+ tkd_exit (2);
+ }
+
+ if (opt.verbose)
+ log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
+
+ return fd;
+}
+
+
+
+/* This is the standard connection thread's main function. */
+static void *
+start_connection_thread (void *arg)
+{
+ ctrl_t ctrl = arg;
+
+ if (ctrl->thread_startup.fd != GNUPG_INVALID_FD
+ && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_nonce))
+ {
+ log_info (_("error reading nonce on fd %d: %s\n"),
+ FD2INT(ctrl->thread_startup.fd), strerror (errno));
+ assuan_sock_close (ctrl->thread_startup.fd);
+ xfree (ctrl);
+ return NULL;
+ }
+
+ active_connections++;
+
+ tkd_init_default_ctrl (ctrl);
+ if (opt.verbose)
+ log_info (_("handler for fd %d started\n"),
+ FD2INT(ctrl->thread_startup.fd));
+
+ /* If this is a pipe server, we request a shutdown if the command
+ handler asked for it. With the next ticker event and given that
+ no other connections are running the shutdown will then
+ happen. */
+ if (tkd_command_handler (ctrl, ctrl->thread_startup.fd)
+ && pipe_server)
+ shutdown_pending = 1;
+
+ if (opt.verbose)
+ log_info (_("handler for fd %d terminated\n"),
+ FD2INT (ctrl->thread_startup.fd));
+
+ tkd_deinit_default_ctrl (ctrl);
+ xfree (ctrl);
+
+ if (--active_connections == 0)
+ tkd_kick_the_loop ();
+
+ return NULL;
+}
+
+
+void
+tkd_kick_the_loop (void)
+{
+ /* Kick the select loop. */
+#ifdef HAVE_W32_SYSTEM
+ int ret = SetEvent (the_event);
+ if (ret == 0)
+ log_error ("SetEvent for tkd_kick_the_loop failed: %s\n",
+ w32_strerror (-1));
+#elif defined(HAVE_PSELECT_NO_EINTR)
+ write (notify_fd, "", 1);
+#else
+ int ret = kill (main_thread_pid, SIGCONT);
+ if (ret < 0)
+ log_error ("sending signal for tkd_kick_the_loop failed: %s\n",
+ gpg_strerror (gpg_error_from_syserror ()));
+#endif
+}
+
+#define TIMERTICK_INTERVAL_SEC (0)
+#define TIMERTICK_INTERVAL_USEC (500000)
+
+/* Connection handler loop. Wait for connection requests and spawn a
+ thread after accepting a connection. LISTEN_FD is allowed to be -1
+ in which case this code will only do regular timeouts and handle
+ signals. */
+static void
+handle_connections (gnupg_fd_t listen_fd)
+{
+ npth_attr_t tattr;
+ struct sockaddr_un paddr;
+ socklen_t plen;
+ fd_set fdset, read_fdset;
+ int nfd;
+ int ret;
+ struct timespec timeout;
+ struct timespec *t;
+ int saved_errno;
+#ifdef HAVE_W32_SYSTEM
+ HANDLE events[2];
+ unsigned int events_set;
+#else
+ int signo;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ int pipe_fd[2];
+
+ ret = gnupg_create_pipe (pipe_fd);
+ if (ret)
+ {
+ log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
+ return;
+ }
+ notify_fd = pipe_fd[1];
+#endif
+
+ ret = npth_attr_init(&tattr);
+ if (ret)
+ {
+ log_error ("npth_attr_init failed: %s\n", strerror (ret));
+ return;
+ }
+
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+#ifdef HAVE_W32_SYSTEM
+ {
+ HANDLE h, h2;
+ SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+
+ events[0] = the_event = INVALID_HANDLE_VALUE;
+ events[1] = INVALID_HANDLE_VALUE;
+ /* Create event for manual reset, initially non-signaled. Make it
+ * waitable and inheritable. */
+ h = CreateEvent (&sa, TRUE, FALSE, NULL);
+ if (!h)
+ log_error ("can't create tkd event: %s\n", w32_strerror (-1) );
+ else if (!DuplicateHandle (GetCurrentProcess(), h,
+ GetCurrentProcess(), &h2,
+ EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
+ {
+ log_error ("setting synchronize for tkd_kick_the_loop failed: %s\n",
+ w32_strerror (-1) );
+ CloseHandle (h);
+ }
+ else
+ {
+ CloseHandle (h);
+ events[0] = the_event = h2;
+ }
+ }
+#endif
+
+ FD_ZERO (&fdset);
+ nfd = 0;
+ if (listen_fd != GNUPG_INVALID_FD)
+ {
+ FD_SET (FD2INT (listen_fd), &fdset);
+ nfd = FD2INT (listen_fd);
+ }
+
+ for (;;)
+ {
+ int max_fd = nfd;
+
+ if (shutdown_pending)
+ {
+ if (active_connections == 0)
+ break; /* ready */
+
+ /* Do not accept anymore connections but wait for existing
+ connections to terminate. We do this by clearing out all
+ file descriptors to wait for, so that the select will be
+ used to just wait on a signal or timeout event. */
+ FD_ZERO (&fdset);
+ listen_fd = GNUPG_INVALID_FD;
+ }
+
+ timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+ timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+
+ if (shutdown_pending)
+ t = &timeout;
+ else
+ t = NULL;
+
+ /* POSIX says that fd_set should be implemented as a structure,
+ thus a simple assignment is fine to copy the entire set. */
+ read_fdset = fdset;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+ FD_SET (pipe_fd[0], &read_fdset);
+ if (max_fd < pipe_fd[0])
+ max_fd = pipe_fd[0];
+#else
+ (void)max_fd;
+#endif
+
+#ifndef HAVE_W32_SYSTEM
+ ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
+ npth_sigev_sigmask ());
+ saved_errno = errno;
+
+ while (npth_sigev_get_pending(&signo))
+ handle_signal (signo);
+#else
+ ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t,
+ events, &events_set);
+ saved_errno = errno;
+ if (events_set & 1)
+ continue;
+#endif
+
+ if (ret == -1 && saved_errno != EINTR)
+ {
+ log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+ strerror (saved_errno));
+ gnupg_sleep (1);
+ continue;
+ }
+
+ if (ret <= 0)
+ /* Timeout. Will be handled when calculating the next timeout. */
+ continue;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+ if (FD_ISSET (pipe_fd[0], &read_fdset))
+ {
+ char buf[256];
+
+ read (pipe_fd[0], buf, sizeof buf);
+ }
+#endif
+
+ if (listen_fd != GNUPG_INVALID_FD
+ && FD_ISSET (FD2INT (listen_fd), &read_fdset))
+ {
+ ctrl_t ctrl;
+ gnupg_fd_t fd;
+
+ plen = sizeof paddr;
+ fd = INT2FD (npth_accept (FD2INT (listen_fd),
+ (struct sockaddr *)&paddr, &plen));
+ if (fd == GNUPG_INVALID_FD)
+ {
+ log_error ("accept failed: %s\n", strerror (errno));
+ }
+ else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
+ {
+ log_error ("error allocating connection control data: %s\n",
+ strerror (errno) );
+ assuan_sock_close (fd);
+ }
+ else
+ {
+ char threadname[50];
+ npth_t thread;
+
+ snprintf (threadname, sizeof threadname, "conn fd=%d",
+ FD2INT (fd));
+ ctrl->thread_startup.fd = fd;
+ ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
+ if (ret)
+ {
+ log_error ("error spawning connection handler: %s\n",
+ strerror (ret));
+ xfree (ctrl);
+ assuan_sock_close (fd);
+ }
+ else
+ npth_setname_np (thread, threadname);
+ }
+ }
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ if (the_event != INVALID_HANDLE_VALUE)
+ CloseHandle (the_event);
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ close (pipe_fd[0]);
+ close (pipe_fd[1]);
+#endif
+ cleanup ();
+ log_info (_("%s %s stopped\n"), gpgrt_strusage(11), gpgrt_strusage(13));
+ npth_attr_destroy (&tattr);
+}
+
+/* Return the number of active connections. */
+int
+get_active_connection_count (void)
+{
+ return active_connections;
+}
diff --git a/tkd/tkdaemon.h b/tkd/tkdaemon.h
new file mode 100644
index 000000000..5d2a15fea
--- /dev/null
+++ b/tkd/tkdaemon.h
@@ -0,0 +1,126 @@
+/* tkdaemon.h - Global definitions for the TKdaemon
+ * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef TKDAEMON_H
+#define TKDAEMON_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT 18 // GPG_ERR_SOURCE_TKD
+#include <gpg-error.h>
+#include <assuan.h>
+
+#include <time.h>
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "../common/sysutils.h"
+
+typedef struct token_ctx_s *token_t;
+
+/* A large struct name "opt" to keep global flags. */
+EXTERN_UNLESS_MAIN_MODULE
+struct
+{
+ unsigned int debug; /* Debug flags (DBG_foo_VALUE). */
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be as quiet as possible. */
+ int dry_run; /* Don't change any persistent data. */
+ int batch; /* Batch mode. */
+ const char *pkcs11_driver; /* Library to access the PKCS#1 module. */
+} opt;
+
+
+#define DBG_APP_VALUE 1 /* Debug app specific stuff. */
+#define DBG_MPI_VALUE 2 /* debug mpi details */
+#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
+#define DBG_TOKEN_VALUE 16 /* debug token info */
+#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE 64 /* debug the caching */
+#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
+#define DBG_HASHING_VALUE 512 /* debug hashing operations */
+#define DBG_IPC_VALUE 1024
+#define DBG_TOKEN_IO_VALUE 2048 /* debug token I/O. */
+
+#define DBG_APP (opt.debug & DBG_APP_VALUE)
+#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
+#define DBG_TOKEN (opt.debug & DBG_TOKEN_VALUE)
+#define DBG_TOKEN_IO (opt.debug & DBG_TOKEN_IO_VALUE)
+
+struct server_local_s;
+
+struct server_control_s
+{
+ /* Private data used to fire up the connection thread. We use this
+ structure do avoid an extra allocation for just a few bytes. */
+ struct {
+ gnupg_fd_t fd;
+ } thread_startup;
+
+ /* Local data of the server; used only in command.c. */
+ struct server_local_s *server_local;
+
+ /* Helper to store the value we are going to sign */
+ struct
+ {
+ unsigned char *value;
+ int valuelen;
+ } in_data;
+};
+
+
+/*-- tkdaemon.c --*/
+void tkd_exit (int rc);
+void tkd_kick_the_loop (void);
+const char *tkd_get_socket_name (void);
+int get_active_connection_count (void);
+
+/*-- command.c --*/
+gpg_error_t initialize_module_command (void);
+int tkd_command_handler (ctrl_t, gnupg_fd_t);
+void send_status_info (ctrl_t ctrl, const char *keyword, ...)
+ GPGRT_ATTR_SENTINEL(1);
+gpg_error_t send_status_direct (ctrl_t ctrl,
+ const char *keyword, const char *args);
+gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword,
+ const char *format, ...) GPGRT_ATTR_PRINTF(3,4);
+void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
+ const char *serialno, const char *idstr,
+ const char *usage);
+
+/*-- pkcs11.c --*/
+gpg_error_t tkd_init (ctrl_t ctrl, assuan_context_t ctx, int rescan);
+gpg_error_t tkd_fini (ctrl_t ctrl, assuan_context_t ctx);
+
+gpg_error_t tkd_sign (ctrl_t ctrl, assuan_context_t ctx,
+ const char *keygrip, int hash_algo,
+ unsigned char **r_outdata,
+ size_t *r_outdatalen);
+gpg_error_t tkd_readkey (ctrl_t ctrl, assuan_context_t ctx,
+ const char *keygrip);
+gpg_error_t tkd_keyinfo (ctrl_t ctrl, assuan_context_t ctx,
+ const char *keygrip, int opt_data, int cap);
+
+
+#endif /*TKDAEMON_H*/