diff options
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | agent/Makefile.am | 2 | ||||
-rw-r--r-- | agent/agent.h | 13 | ||||
-rw-r--r-- | agent/call-tkd.c | 370 | ||||
-rw-r--r-- | agent/command.c | 80 | ||||
-rw-r--r-- | agent/divert-tkd.c | 45 | ||||
-rw-r--r-- | agent/findkey.c | 66 | ||||
-rw-r--r-- | agent/gpg-agent.c | 6 | ||||
-rw-r--r-- | agent/keyformat.txt | 12 | ||||
-rw-r--r-- | agent/pksign.c | 4 | ||||
-rw-r--r-- | agent/protect.c | 22 | ||||
-rw-r--r-- | am/cmacros.am | 3 | ||||
-rw-r--r-- | common/mapstrings.c | 1 | ||||
-rw-r--r-- | common/util.h | 1 | ||||
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | tkd/Makefile.am | 37 | ||||
-rw-r--r-- | tkd/command.c | 682 | ||||
-rw-r--r-- | tkd/pkcs11.c | 1407 | ||||
-rw-r--r-- | tkd/pkcs11.h | 1384 | ||||
-rw-r--r-- | tkd/tkdaemon.c | 1293 | ||||
-rw-r--r-- | tkd/tkdaemon.h | 126 |
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*/ |