mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-21 10:09:57 +01:00
Start tkdaemon to offer PKCS#11 module access.
Currently, it's a mock-up. Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
parent
34abc6cd9b
commit
d30ce02dd6
@ -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
|
||||
|
@ -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 },
|
||||
|
@ -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);
|
||||
|
18
configure.ac
18
configure.ac
@ -187,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)],
|
||||
@ -1870,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
|
||||
@ -1907,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])
|
||||
@ -1936,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",
|
||||
@ -2131,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
|
||||
@ -2143,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
|
||||
|
@ -18,9 +18,7 @@
|
||||
|
||||
EXTRA_DIST =
|
||||
|
||||
# libexec_PROGRAMS = tkdaemon
|
||||
|
||||
noinst_PROGRAMS = pksign
|
||||
libexec_PROGRAMS = tkdaemon
|
||||
|
||||
AM_CPPFLAGS =
|
||||
|
||||
@ -31,7 +29,7 @@ include $(top_srcdir)/am/cmacros.am
|
||||
|
||||
tkdaemon_SOURCES = \
|
||||
tkdaemon.c tkdaemon.h \
|
||||
command.c
|
||||
command.c pkcs11.c
|
||||
|
||||
tkdaemon_LDADD = $(libcommonpth) \
|
||||
$(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
|
||||
|
695
tkd/command.c
Normal file
695
tkd/command.c
Normal file
@ -0,0 +1,695 @@
|
||||
/* 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 <assuan.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;
|
||||
|
||||
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
|
||||
|
||||
/* 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 = 0;
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
(void)ctrl;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const char hlp_readkey[] =
|
||||
"READKEY [--format=advanced|ssh] [--info[-only]] <keygrip>\n"
|
||||
"\n"
|
||||
"Return the public key for the given cert or key ID as a standard\n"
|
||||
"S-expression. With --format option, it may be returned in advanced\n"
|
||||
"S-expression format, or SSH format. With --info a KEYPAIRINFO\n"
|
||||
"status line is also emitted; with --info-only the regular output is\n"
|
||||
"suppressed.";
|
||||
static gpg_error_t
|
||||
cmd_readkey (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
int advanced = 0;
|
||||
int ssh = 0;
|
||||
int opt_info = 0;
|
||||
int opt_nokey = 0;
|
||||
unsigned char *pk = NULL;
|
||||
size_t pklen;
|
||||
token_t token;
|
||||
const char *keygrip;
|
||||
|
||||
if (has_option (line, "--format=advanced"))
|
||||
advanced = 1;
|
||||
if (has_option (line, "--format=ssh"))
|
||||
ssh = 1;
|
||||
if (has_option (line, "--info"))
|
||||
opt_info = 1;
|
||||
if (has_option (line, "--info-only"))
|
||||
opt_info = opt_nokey = 1;
|
||||
|
||||
keygrip = skip_options (line);
|
||||
if (strlen (keygrip) != 40)
|
||||
err = gpg_error (GPG_ERR_INV_ID);
|
||||
|
||||
token = token_get (ctrl, keygrip);
|
||||
if (token)
|
||||
{
|
||||
err = token_readkey (token, opt_info, &pk, &pklen);
|
||||
token_put (token);
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_SECKEY);
|
||||
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
if (opt_nokey)
|
||||
;
|
||||
else if (ssh)
|
||||
{
|
||||
estream_t stream = NULL;
|
||||
gcry_sexp_t s_key;
|
||||
void *buf = NULL;
|
||||
size_t buflen;
|
||||
|
||||
stream = es_fopenmem (0, "r+b");
|
||||
if (!stream)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = gcry_sexp_new (&s_key, pk, pklen, 0);
|
||||
if (err)
|
||||
{
|
||||
es_fclose (stream);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = ssh_public_key_in_base64 (s_key, stream, "(none)");
|
||||
if (err)
|
||||
{
|
||||
gcry_sexp_release (s_key);
|
||||
es_fclose (stream);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = es_fclose_snatch (stream, &buf, &buflen);
|
||||
gcry_sexp_release (s_key);
|
||||
if (!err)
|
||||
err = assuan_send_data (ctx, buf, buflen);
|
||||
}
|
||||
else if (advanced)
|
||||
{
|
||||
gcry_sexp_t s_key;
|
||||
unsigned char *pkadv;
|
||||
size_t pkadvlen;
|
||||
|
||||
err = gcry_sexp_new (&s_key, pk, pklen, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
pkadvlen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, NULL, 0);
|
||||
pkadv = xtrymalloc (pkadvlen);
|
||||
if (!pkadv)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
gcry_sexp_release (s_key);
|
||||
goto leave;
|
||||
}
|
||||
log_assert (pkadvlen);
|
||||
|
||||
gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, pkadv, pkadvlen);
|
||||
gcry_sexp_release (s_key);
|
||||
/* (One less to adjust for the trailing '\0') */
|
||||
err = assuan_send_data (ctx, pkadv, pkadvlen-1);
|
||||
xfree (pkadv);
|
||||
}
|
||||
else
|
||||
err = assuan_send_data (ctx, pk, pklen);
|
||||
|
||||
leave:
|
||||
xfree (pk);
|
||||
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;
|
||||
token_t token;
|
||||
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");
|
||||
|
||||
keygrip = skip_options (line);
|
||||
|
||||
if (strlen (keygrip) != 40)
|
||||
err = gpg_error (GPG_ERR_INV_ID);
|
||||
|
||||
token = token_get (ctrl, keygrip);
|
||||
if (token)
|
||||
{
|
||||
err = token_sign (token, keygrip, hash_algo, &outdata, &outdatalen);
|
||||
token_put (token);
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_SECKEY);
|
||||
|
||||
if (err)
|
||||
{
|
||||
log_error ("token_sign failed: %s\n", gpg_strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
err = assuan_send_data (ctx, outdata, outdatalen);
|
||||
xfree (outdata);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int cap;
|
||||
int opt_data;
|
||||
const char *keygrip = NULL;
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
opt_data = has_option (line, "--data");
|
||||
|
||||
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);
|
||||
|
||||
token_keyinfo (ctrl, keygrip, opt_data, cap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* 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 },
|
||||
{ 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;
|
||||
}
|
@ -213,6 +213,9 @@ notify_cb (ck_session_handle_t session,
|
||||
struct p11dev *priv = application;
|
||||
|
||||
(void)priv;
|
||||
(void)session;
|
||||
(void)event;
|
||||
(void)application;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -224,7 +227,6 @@ open_session (struct token *token)
|
||||
ck_slot_id_t slot_id = token->slot_id;
|
||||
ck_session_handle_t session_handle;
|
||||
ck_flags_t session_flags;
|
||||
int option = 0;
|
||||
|
||||
session_flags = CKU_USER;
|
||||
// session_flags = session_flags | CKF_RW_SESSION;
|
||||
@ -234,7 +236,7 @@ open_session (struct token *token)
|
||||
(void *)&p11_priv, notify_cb, &session_handle);
|
||||
if (err)
|
||||
{
|
||||
printf ("open_session: %d\n", err);
|
||||
log_debug ("open_session: %ld\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -308,7 +310,6 @@ examine_public_key (struct token *token, struct key *k, unsigned long keytype,
|
||||
unsigned char ecpoint[256];
|
||||
struct ck_attribute templ[3];
|
||||
unsigned long mechanisms[3];
|
||||
int i;
|
||||
|
||||
if (keytype == CKK_RSA)
|
||||
{
|
||||
@ -340,9 +341,9 @@ examine_public_key (struct token *token, struct key *k, unsigned long keytype,
|
||||
}
|
||||
|
||||
/* Found a RSA key. */
|
||||
printf ("RSA: %d %d\n",
|
||||
templ[0].ulValueLen,
|
||||
templ[1].ulValueLen);
|
||||
log_debug ("RSA: %ld %ld\n",
|
||||
templ[0].ulValueLen,
|
||||
templ[1].ulValueLen);
|
||||
puts ("Public key:");
|
||||
compute_keygrip_rsa (k->keygrip,
|
||||
modulus, templ[0].ulValueLen,
|
||||
@ -377,9 +378,9 @@ examine_public_key (struct token *token, struct key *k, unsigned long keytype,
|
||||
}
|
||||
|
||||
/* Found an ECC key. */
|
||||
printf ("ECC: %d %d\n",
|
||||
templ[0].ulValueLen,
|
||||
templ[1].ulValueLen);
|
||||
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);
|
||||
@ -400,7 +401,7 @@ examine_public_key (struct token *token, struct key *k, unsigned long keytype,
|
||||
if (templ[0].ulValueLen)
|
||||
{
|
||||
/* Scute works well. */
|
||||
printf ("mechanism: %x %d\n", mechanisms[0], templ[0].ulValueLen);
|
||||
log_debug ("mechanism: %lx %ld\n", mechanisms[0], templ[0].ulValueLen);
|
||||
k->mechanism = mechanisms[0];
|
||||
}
|
||||
else
|
||||
@ -434,7 +435,6 @@ detect_private_keys (struct token *token)
|
||||
|
||||
unsigned long cnt = 0;
|
||||
ck_object_handle_t obj;
|
||||
int i;
|
||||
|
||||
class = CKO_PRIVATE_KEY;
|
||||
templ[0].type = CKA_CLASS;
|
||||
@ -484,8 +484,8 @@ detect_private_keys (struct token *token)
|
||||
k->id_len = templ[2].ulValueLen;
|
||||
k->id[k->id_len] = 0;
|
||||
|
||||
printf ("slot: %x handle: %ld label: %s key_type: %d id: %s\n",
|
||||
token->slot_id, obj, k->label, keytype, k->id);
|
||||
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;
|
||||
@ -573,7 +573,7 @@ check_public_keys (struct token *token)
|
||||
if (i == token->num_keys)
|
||||
continue;
|
||||
|
||||
printf ("pub: slot: %x handle: %ld label: %s key_type: %d id: %s\n",
|
||||
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))
|
||||
@ -588,6 +588,7 @@ check_public_keys (struct token *token)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static long
|
||||
get_certificate (struct token *token)
|
||||
{
|
||||
@ -652,11 +653,11 @@ get_certificate (struct token *token)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static long
|
||||
learn_keys (struct token *token)
|
||||
{
|
||||
unsigned long err = 0;
|
||||
int i;
|
||||
|
||||
/* Detect private keys on the token.
|
||||
@ -773,7 +774,7 @@ do_pksign (struct key *key,
|
||||
key->p11_keyid);
|
||||
if (err)
|
||||
{
|
||||
printf ("C_SignInit error: %d", err);
|
||||
log_error ("C_SignInit error: %ld", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -786,7 +787,7 @@ do_pksign (struct key *key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TESTING
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
@ -896,9 +897,4 @@ main (int argc, const char *argv[])
|
||||
ck->f->C_Finalize (NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
cc -g -o test_tk pksign.c -lgcrypt
|
||||
./test_tk /usr/lib/softhsm/libsofthsm2.so <KEYGRIP> 5678
|
||||
./test_tk /usr/local/lib/x86_64-linux-gnu/scute.so <KEYGRIP>
|
||||
*/
|
||||
#endif
|
1295
tkd/tkdaemon.c
Normal file
1295
tkd/tkdaemon.c
Normal file
File diff suppressed because it is too large
Load Diff
110
tkd/tkdaemon.h
Normal file
110
tkd/tkdaemon.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* 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 <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);
|
||||
|
||||
/*-- 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);
|
||||
|
||||
#endif /*TKDAEMON_H*/
|
Loading…
x
Reference in New Issue
Block a user