Page MenuHome GnuPG

No OneTemporary

diff --git a/ChangeLog b/ChangeLog
index 77736a8..dabe5e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,347 +1,369 @@
+2008-09-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * src/agent.c: Include "cert.h".
+ (GET_CERT_INIT_SIZE): New symbol.
+ (struct get_cert_s): New struct.
+ (get_cert_data_cb, scute_agent_get_cert): New functions.
+ * src/gpgsm.c: Include "agent.h".
+ (scute_gpgsm_get_cert): Take extra argument NO. Use it to get the
+ certificate directly from the card, if possible.
+ * src/gpgsm.h (scute_gpgsm_get_cert): Add extra argument NO to
+ prototype.
+ * src/slots.c (slot_init): Pass extra argument to
+ scute_gpgsm_get_cert invocation.
+ * src/cert-object.c (scute_attr_prv, scute_attr_cert): Don't use
+ the fpr, timestamp and expire field of a certificate for now.
+
+2008-09-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * src/agent.c (GET_CERT_INIT_SIZE): New symbol.
+ (struct get_cert_s): New struct.
+ (get_cert_data_cb, scute_agent_get_cert): New functions.
+
2008-09-03 Marcus Brinkmann <marcus@g10code.com>
* src/Makefile.am (.rc.o): New rule.
* src/Makefile.am (EXTRA_DIST): Add stpcpy.h.
2008-09-02 Marcus Brinkmann <marcus@g10code.com>
* configure.ac: Post-release cleanup.
* doc/website/download.xhtml: Update.
2008-09-02 Marcus Brinkmann <marcus@g10code.com>
* Released version 1.2.0.
* doc/manual/Makefile.am (CLEANFILES): Add $(images_eps).
(images_eps): New variable.
(scute.dvi): Depend on $(images_eps) instead of $(images_pdf).
2008-08-23 Marcus Brinkmann <marcus@g10code.com>
* src/Makefile.am [HAVE_W32_SYSTEM]: Build autonomous DLL.
2008-08-23 Marcus Brinkmann <marcus@g10code.com>
* src/agent.c (gnupg_allow_set_foregound_window)
[!HAVE_W32_SYSTEM]: Define it.
2008-08-23 Marcus Brinkmann <marcus@g10code.com>
* src/Makefile.am [HAVE_W32_SYSTEM]: Build autonomous DLL.
* src/dllmain.c (DllMain): New file.
* src/cert-gpgsm.c: Include "debug.h". Use
assuan_pipe_connect_ext instead of assuan_pipe_connect everywhere.
* src/agent.c (WINVER) [HAVE_W32_SYSTEM]: Define symbol.
[HAVE_W32_SYSTEM]: Include <windows.h>. Do this before including
<assuan.h>.
(gnupg_allow_set_foregound_window): New function.
(agent_connect): Use Sleep() instead of _sleep().
(default_inq_cb): New function, and use it in all transactions.
(agent_configure): Don't bail out if ttytname is not defined. Set
allow-pinentry-notify.
* tests/t-support.h [_WIN32]: Include <windows.h>.
(init_cryptoki) [_WIN32]: Call WSAStartup.
2008-08-21 Marcus Brinkmann <marcus@g10code.com>
* src/scute.def: Start at @1 instead of @0.
2008-08-21 Marcus Brinkmann <marcus@g10code.com>
* src/agent.c (agent_configure): Fix cut and paste error.
2008-08-11 Marcus Brinkmann <marcus@g10code.com>
* src/agent.c (spawn_process_detachted) [! HAVE_W32_SYSTEM]: Remove.
(agent_configure): Ignore error with old versions of gpg-agent.
2008-08-08 Marcus Brinkmann <marcus@g10code.de>
* configure.ac: Update svn macros.
(BUILD_REVISION, BUILD_FILEVERSION, BUILD_TIMESTAMP): New.
(AC_CONFIG_OUTPUT): Add src/versioninfo.rc.
(GPG_AGENT_DEFAULT, GPG_AGENT): New.
(AC_REPLACE_FUNCS): Add vasprintf and stpcpy.
(AC_CHECK_FUNCS): Add ttyname, localtime_r and timegm.
* src/stpcpy.h, src/stpcpy.c, src/realloc.c: New file from gnulib.
* src/vasprintf.c: New file from libiberty.
* src/support.h [!HAVE_STPCPY]: Include "stpcpy.h".
[!HAVE_TTYNAME]: Define simple replacement function.
(get_gpgsm_path, get_gpg_agent_path)
(default_homedir, make_filename): New prototypes.
* src/Makefile.am: Add W32 support.
(libscute_la_SOURCES): Add get-path.c.
(EXTRA_DIST): Add versioninfo.rc.in.
* src/versioninfo.rc.in: New file.
* src/get-path.c: New file.
* src/agent.c (PATHSEP_C): New macro.
(build_w32_commandline_copy)
(build_w32_commandline) [HAVE_W32_SYSTEM]: New functions.
(spawn_process_deatched): New function.
(agent_connect): Start gpg-agent if it is not running yet.
(agent_configure): Also pass xauthority and pinentry-user-data.
* src/cert-gpgsm.c (COMPAT_FALLBACK) [!HAVE_W32_SYSTEM]: New
macro.
(export_cert_compat) [!COMPAT_FALLBACK]: Remove.
(export_cert) [!COMPAT_FALLBACK]: Don't call export_cert_compat.
Don't create (unused) output pipe cruft.
* src/cert-object.c (time_to_ck_date) [!HAVE_LOCALTIME_R]: Fall
back to localtime.
* tests/t-getslotinfo.c (main) [WIN32]: Call _sleep instead sleep.
* src/cryptoki.h (CRYPTOKI_EXPORTS): Define symbol.
* src/error-mapping.h: Do not include <error.h>.
(scute_sys_to_ck): Change type of ERR to int.
* src/error-mapping.c: Do not include <error.h>.
(scute_sys_to_ck): Change type of ERR to int.
* src/slots.c (slot_create_session): Change type of ERR to int.
2007-05-03 Marcus Brinkmann <marcus@g10code.de>
Released version 1.1.0.
* doc/manual/Makefile.am (images_pdf, CLEANFILES): New variables.
(scute.dvi, scute.pdf): New dependencies.
(%.eps, %.pdf): New rules.
* configure.ac: Check for convert and epstopdf.
* doc/manual/scute.texi (Authentication With Service): Avoid
@indicateurl in TeX output, as this seems to be broken (in
texi2dvi 1.34).
* doc/manual/Makefile.am (images, EXTRA_DIST): New variables.
2007-04-30 Marcus Brinkmann <marcus@g10code.de>
* src/agent.h (scute_agent_is_trusted): New prototype. Include
<stdbool.h>.
* src/agent.c (scute_agent_is_trusted): New implementation.
* src/cert.h (struct cert): New member IS_TRUSTED.
* src/cert-gpgsm.c (export_cert): Set CERT->is_trusted.
* src/cert-object.c (scute_attr_cert): Set CKA_TRUSTED to
CERT->is_trusted.
* tests/t-getattribute.c: Support new option '--printable'.
* src/table.c (scute_table_dealloc): Return, but not a value.
2007-02-09 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h: Add definitions for SHA.
2006-12-17 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h: Minor cosmetic changes. Require CRYPTOKI_GNU now
to switch on the GNU API, and do not check what CRYPTOKI_COMPAT is
defined to, just if it is defined at all.
* src/pkcs11.h [__WIN32]: Changed to [_WIN32 ||
CRYPTOKI_FORCE_WIN32] at the end.
Submitted by Alon Bar-Lev.
2006-12-11 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h (CKO_VENDOR_DEFINED): Fixed syntax error.
2006-12-10 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (NEED_GPG_ERROR_VERSION): Bump to 1.4.
* src/pkcs11.h [CRYPTOKI_COMPAT]: Rewrote the compatibility layer.
2006-12-09 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h [__cplusplus]: Protect with extern "C".
Do not use the C++ keyword "template" in parameter lists.
Submitted by Alon Bar-Lev.
* src/pkcs11.h [__WIN32]: Changed to [_WIN32 ||
CRYPTOKI_FORCE_WIN32].
(ck_rv_t): Changed to unsigned long.
(CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR,
CRYPTOKI_VERSION_REVISION): New macros.
(CKO_VENDOR_DEFINED, CKH_VENDOR_DEFINED, CKK_VENDOR_DEFINED,
CKC_VENDOR_DEFINED, CKA_VENDOR_DEFINED, CKM_VENDOR_DEFINED,
CKF_EXTENSION, CKR_VENDOR_DEFINED): Add cast to suppress compiler
warning.
Submitted by Alon Bar-Lev.
* src/cryptoki.h (CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR):
Removed.
2006-11-30 Marcus Brinkmann <marcus@g10code.de>
* m4/autobuild.m4: New file.
* configure.ac: Invoke AB_INIT.
2006-11-29 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (CFLAGS): Remove variable.
2006-11-27 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h: Add more definitions provided by Andreas Jellinghaus.
* README (Copyright and License): Update copyright notice. Also
for all other files.
* AUTHORS (Maintainer): Removed RSA Security Inc.
* include/pkcs11t.h, include/pkcs11f.h, include/pkcs11.h,
include/disclaimer.txt, include/cryptoki-w32.h,
include/cryptoki-generic.h, include/README, include/Makefile.am:
Files removed.
* configure.ac (AC_CONFIG_FILES): Removed include/Makefile.
* Makefile.am (SUBDIRS): Removed include directory.
2006-11-24 Marcus Brinkmann <marcus@g10code.de>
* src/pkcs11.h: New file.
* src/Makefile.am (libscute_la_SOURCES): Add pkcs11.h.
* src/cryptoki.h: Include "pkcs11.h" instead of original PKCS #11
header file.
* tests/t-support.h (mechanism_type_str): Remove most mechanisms.
2006-11-23 Marcus Brinkmann <marcus@g10code.de>
* src/cert-gpgsm.c (search_certs_line): Don't overwrite CERT->uid.
2006-11-21 Marcus Brinkmann <marcus@g10code.de>
* src/cert-gpgsm.c (search_certs_line): Fix off-by-one error.
(export_cert_cb): Fix bug if EXP->buffer_size is 0.
* src/cert-gpgsm.c (cert_reset): Free CERT->cert_der if set.
(export_cert): Reimplement using data channel.
(export_cert_compat): This contains the old version for
compatibility.
* src/cert.h (MAX_CERT_SIZE): Macro removed.
(struct cert): Changed member CERT_DER into a pointer.
* configure.ac (AC_CONFIG_FILES): Add doc/manual/Makefile.
* doc/Makefile.am (SUBDIRS): New variable.
* doc/manual/Makefile, doc/manual/scute.texi, doc/manual/gpl.texi,
doc/manual/mdate-sh, doc/manual/texinfo.tex,
doc/manual/firefox-cm.png, doc/manual/firefox-cm-view-detail.png,
doc/manual/firefox-cm-view.png,
doc/manual/firefox-dm-load-after.png,
doc/manual/firefox-dm-load-before.png,
doc/manual/firefox-dm-load.png,
doc/manual/firefox-dm-token-present.png,
doc/manual/firefox-pref.png, doc/manual/firefox-pref-view.png: New
files.
2006-11-11 Marcus Brinkmann <marcus@g10code.de>
* Released version 1.0.0.
2006-11-11 Marcus Brinkmann <marcus@g10code.de>
* src/error-mapping.c (scute_gpg_err_to_ck): Report error on debug
stream.
* src/slots.c (add_object): New function.
(slot_init): Rewritten using add_object.
* src/gpgsm.c: Include "gpgsm.h".
(struct search): Replace members ATTRP, ATTR_COUNTP, PRV_ATTRP,
PRV_ATTR_COUNTP by CERT_GET_CB, HOOK.
(search_cb): Rewritten to add all certificates for a certain key
(scute_gpgsm_get_cert): Take a callback function instead of
attribute pointers.
* gpgsm.h (scute_gpgsm_get_cert): Adjust prototype.
and also the certificate chain.
* src/cert.h (scute_gpgsm_search_certs_by_fpr): New prototype.
(struct cert): New member chain_id.
* src/cert-gpgsm.c (struct search_ctx_by_grip): Rename to ...
(struct search_ctx): ... this. Rename field GRIP to PATTERN, and
add new field FIELD.
(search_certs_by_grip): Rename function to ...
(search_certs): ... this.
(scute_gpgsm_search_certs_by_fpr): Change user of the above.
(scute_gpgsm_search_certs_by_fpr): New function.
(search_certs_line): Store chain ID.
2006-11-07 Marcus Brinkmann <marcus@g10code.de>
* src/p11-gettokeninfo.c (C_GetTokenInfo): Fix assignment.
2006-11-06 Marcus Brinkmann <marcus@g10code.de>
* doc/website/format/web.css: Set margin to 0.
* configure.ac (AC_CONFIG_FILES): Add doc/Makefile.
Makefile.am (SUBDIRS): Add doc/.
doc/, doc/website/, doc/website/format: New directories.
doc/Makefile.am, doc/website/index.xhtml,
doc/website/documentation.xhtml, doc/website/download.xhtml,
doc/website/contact.xhtml, doc/website/format/web.css,
doc/website/format/scute-logo.svg,
doc/website/format/scute-border.jpg: New files.
2006-10-30 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (PACKAGE, VERSION): Define these variables.
* src/Makefile.am (libscute_la_CPPFLAGS): Fix include path for
VPATH builds.
* configure.ac (VERSION_MAJOR, VERSION_MINOR): New variable
definitions.
* src/gpgsm.h: Fix syntax error.
* src/table.c, src/table.h: Rewritten.
* src/slots.c: Reorganized and rewritten to use the new table
interface. Include gpgsm.h.
* src/p11-signinit.c, src/p11-sign.c, src/p11-getslotlist.c,
src/p11-getsessioninfo.c, src/p11-getmechanismlist.c,
src/p11-getattributevalue.c, src/p11-findobjectsinit.c,
src/p11-findobjectsfinal.c, src/p11-findobjects.c,
src/p11-closesession.c: Adjust callers.
2006-10-26 Marcus Brinkmann <marcus@g10code.de>
* src/settings.h: Require VERSION_MAJOR and VERSION_MINOR to be
defined.
* src/p11-getinfo.c (CRYPTOKI_VERSION_MAJOR,
CRYPTOKI_VERSION_MINOR): Move macros to ...
* src/cryptoki.h (CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR):
... here
* src/gpgsm.c (gpgsm_get_cert): Rename to ...
(scute_gpgsm_get_cert): ... this.
* src/gpgsm.h: New file.
* src/Makefile.am (libscute_la_SOURCES): Add gpgsm.h.
* src/slots.c (gpgsm_get_cert): Remove prototype.
(slots_update_slot): Call scute_gpgsm_get_cert, not gpgsm_get_cert.
* src/error-mapping.c (scute_gpg_err_to_ck): Map GPG_ERR_NO_AGENT
to CKR_GENERAL_ERROR.
* src/agent.h (struct agent_card_info_s): Remove member error.
Prefix external functions with scute_ if not already done so, also
for callers.
* src/agent.c: Include <stdarg.h>. Replace one-letter variable
names by proper ones. Replace assuan_error_t with gpg_error_t
everywhere.
(agent_connect, agent_simple_cmd, agent_configure): New functions.
(scute_agent_initialize): Use these new functions.
ing of common code. Lots of cleanups.
(scute_agent_finalize): Clear agent_ctx. Add debug message.
2006-10-13 Werner Koch <wk@g10code.com>
* src/cert-gpgsm.c (MAX_LINE_LEN): Increased.
* src/locking.c: Include string.h for memset declaration.
* configure.ac: New option --disable-optimization.
2006-08-02 Marcus Brinkmann <marcus@g10code.de>
* Initial version.
Copyright 2006, 2007, 2008 g10 Code GmbH
This file is free software; as a special exception the author gives
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.
diff --git a/NEWS b/NEWS
index 551d262..9afc3f0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,44 +1,44 @@
Noteworthy changes in version 1.3.0 (unreleased)
------------------------------------------------
- * ...
+ * Scute can read certificates directly from the OpenPGP 2.0 cards.
Noteworthy changes in version 1.2.0 (2008-09-02)
------------------------------------------------
* Ported to Windows 32.
* GPG Agent can now be launched on demand.
Noteworthy changes in version 1.1.0 (2007-05-03)
------------------------------------------------
* Scute now comes with a manual.
* Code licensed from RSA Security Inc. has been removed due to
licensing issues ("advertisment clause").
* A memory leak has been fixed. Note that you need libassuan 1.0.1
to fix another memory leak.
* Scute now supports certificates larger than the kernel pipe buffer
with GPGSM versions later than 2.0.0 (exclusive).
* Scute now sets the CKA_TRUSTED attribute to something useful.
Noteworthy changes in version 1.0.0 (2006-11-11)
------------------------------------------------
* Initial release.
Copyright 2006 g10 Code GmbH
This file is free software; as a special exception the author gives
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.
diff --git a/src/agent.c b/src/agent.c
index dc6d8dd..3cd8eaa 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -1,976 +1,1057 @@
/* agent.c - Talking to gpg-agent.
Copyright (C) 2006, 2007, 2008 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#ifdef HAVE_W32_SYSTEM
#define PATHSEP_C ';'
#define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */
#include <windows.h>
#else
#define PATHSEP_C ':'
#endif
#include <assuan.h>
#include <gpg-error.h>
#include "debug.h"
#include "support.h"
+#include "cert.h"
#include "agent.h"
/* The global agent context. */
static assuan_context_t agent_ctx = NULL;
/* Hack required for Windows. */
void
gnupg_allow_set_foregound_window (pid_t pid)
{
if (!pid || pid == (pid_t)(-1))
return;
#ifdef HAVE_W32_SYSTEM
else if (!AllowSetForegroundWindow (pid))
DEBUG ("AllowSetForegroundWindow(%lu) failed: %i\n",
(unsigned long)pid, GetLastError ());
#endif
}
#ifdef HAVE_W32_SYSTEM
/* Helper function to build_w32_commandline. */
static char *
build_w32_commandline_copy (char *buffer, const char *string)
{
char *p = buffer;
const char *s;
if (!*string) /* Empty string. */
p = stpcpy (p, "\"\"");
else if (strpbrk (string, " \t\n\v\f\""))
{
/* Need top do some kind of quoting. */
p = stpcpy (p, "\"");
for (s=string; *s; s++)
{
*p++ = *s;
if (*s == '\"')
*p++ = *s;
}
*p++ = '\"';
*p = 0;
}
else
p = stpcpy (p, string);
return p;
}
/* Build a command line for use with W32's CreateProcess. On success
CMDLINE gets the address of a newly allocated string. */
static gpg_error_t
build_w32_commandline (const char *pgmname, const char * const *argv,
char **cmdline)
{
int i, n;
const char *s;
char *buf, *p;
*cmdline = NULL;
n = 0;
s = pgmname;
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
for (; *s; s++)
if (*s == '\"')
n++; /* Need to double inner quotes. */
for (i=0; (s=argv[i]); i++)
{
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
for (; *s; s++)
if (*s == '\"')
n++; /* Need to double inner quotes. */
}
n++;
buf = p = malloc (n);
if (!buf)
return gpg_error_from_syserror ();
p = build_w32_commandline_copy (p, pgmname);
for (i=0; argv[i]; i++)
{
*p++ = ' ';
p = build_w32_commandline_copy (p, argv[i]);
}
*cmdline= buf;
return 0;
}
/* Spawn a new process and immediatley detach from it. The name of
the program to exec is PGMNAME and its arguments are in ARGV (the
programname is automatically passed as first argument). An error
is returned if pgmname is not executable; to make this work it is
necessary to provide an absolute file name. All standard file
descriptors are connected to /dev/null. */
static gpg_error_t
spawn_process_detached (const char *pgmname, const char *argv[])
{
gpg_error_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
int cr_flags;
char *cmdline;
if (access (pgmname, X_OK))
return gpg_error_from_syserror ();
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
/* Start the process. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_MINIMIZE;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_NEW_PROCESS_GROUP
| DETACHED_PROCESS);
DEBUG ("CreateProcess(detached), path=`%s' cmdline=`%s'\n",
pgmname, cmdline);
if (!CreateProcess (pgmname, /* Program to start. */
cmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
FALSE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
))
{
DEBUG ("CreateProcess(detached) failed: %i\n", GetLastError ());
free (cmdline);
return gpg_error (GPG_ERR_GENERAL);
}
free (cmdline);
cmdline = NULL;
DEBUG ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
CloseHandle (pi.hThread);
return 0;
}
#endif
/* Establish a connection to a running GPG agent. */
static gpg_error_t
agent_connect (assuan_context_t *ctx_r)
{
/* If we ever failed to connect via a socket we will force the use
of the pipe based server for the lifetime of the process. */
static int force_pipe_server = 0;
gpg_error_t err = 0;
char *infostr;
char *ptr;
restart:
infostr = force_pipe_server ? NULL : getenv ("GPG_AGENT_INFO");
if (!infostr || !*infostr)
{
char *sockname;
/* First check whether we can connect at the standard
socket. */
sockname = make_filename (default_homedir (), "S.gpg-agent", NULL);
if (! sockname)
return gpg_error_from_errno (errno);
err = assuan_socket_connect (ctx_r, sockname, 0);
if (err)
{
const char *agent_program;
/* With no success start a new server. */
DEBUG ("no running GPG agent at %s, starting one\n", sockname);
agent_program = get_gpg_agent_path ();
#ifdef HAVE_W32_SYSTEM
{
/* Under Windows we start the server in daemon mode. This
is because the default is to use the standard socket
and thus there is no need for the GPG_AGENT_INFO
envvar. This is possible as we don't have a real unix
domain socket but use a plain file and thus there is no
need to care about non-local file systems. */
const char *argv[3];
argv[0] = "--daemon";
argv[1] = "--use-standard-socket";
argv[2] = NULL;
err = spawn_process_detached (agent_program, argv);
if (err)
DEBUG ("failed to start agent `%s': %s\n",
agent_program, gpg_strerror (err));
else
{
/* Give the agent some time to prepare itself. */
Sleep (3 * 1000);
/* Now try again to connect the agent. */
err = assuan_socket_connect (ctx_r, sockname, 0);
}
}
#else /*!HAVE_W32_SYSTEM*/
{
const char *pgmname;
const char *argv[3];
int no_close_list[3];
int i;
if ( !(pgmname = strrchr (agent_program, '/')))
pgmname = agent_program;
else
pgmname++;
argv[0] = pgmname;
argv[1] = "--server";
argv[2] = NULL;
i=0;
no_close_list[i++] = fileno (stderr);
no_close_list[i] = -1;
/* Connect to the agent and perform initial handshaking. */
err = assuan_pipe_connect (ctx_r, agent_program, argv,
no_close_list);
}
#endif /*!HAVE_W32_SYSTEM*/
}
free (sockname);
}
else
{
int pid;
int protocol_version;
infostr = strdup (infostr);
if (!infostr)
return gpg_error_from_errno (errno);
if (!(ptr = strchr (infostr, PATHSEP_C)) || ptr == infostr)
{
DEBUG ("malformed GPG_AGENT_INFO environment variable");
free (infostr);
force_pipe_server = 1;
goto restart;
}
*(ptr++) = 0;
pid = atoi (ptr);
while (*ptr && *ptr != PATHSEP_C)
ptr++;
protocol_version = *ptr ? atoi (ptr + 1) : 0;
if (protocol_version != 1)
{
DEBUG ("GPG agent protocol version '%d' not supported",
protocol_version);
free (infostr);
force_pipe_server = 1;
goto restart;
}
err = assuan_socket_connect (ctx_r, infostr, pid);
free (infostr);
if (err)
{
DEBUG ("cannot connect to GPG agent: %s", gpg_strerror (err));
force_pipe_server = 1;
goto restart;
}
}
if (err)
{
DEBUG ("cannot connect to GPG agent: %s", gpg_strerror (err));
return gpg_error (GPG_ERR_NO_AGENT);
}
return 0;
}
/* This is the default inquiry callback. It mainly handles the
Pinentry notifications. */
static int
default_inq_cb (void *opaque, const char *line)
{
(void)opaque;
if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
{
gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
/* We do not pass errors to avoid breaking other code. */
}
else
DEBUG ("ignoring gpg-agent inquiry `%s'\n", line);
return 0;
}
/* Send a simple command to the agent. */
static gpg_error_t
agent_simple_cmd (assuan_context_t ctx, const char *fmt, ...)
{
gpg_error_t err;
char *optstr;
va_list arg;
int res;
va_start (arg, fmt);
res = vasprintf (&optstr, fmt, arg);
va_end (arg);
if (res < 0)
return gpg_error_from_errno (errno);
err = assuan_transact (ctx, optstr, NULL, NULL, default_inq_cb,
NULL, NULL, NULL);
if (err)
DEBUG ("gpg-agent command '%s' failed: %s", optstr, gpg_strerror (err));
free (optstr);
return err;
}
/* Configure the GPG agent at connection CTX. */
static gpg_error_t
agent_configure (assuan_context_t ctx)
{
gpg_error_t err = 0;
char *dft_display = NULL;
char *dft_ttyname = NULL;
char *dft_ttytype = NULL;
#if defined(HAVE_SETLOCALE) && (defined(LC_CTYPE) || defined(LC_MESSAGES))
char *old_lc = NULL;
char *dft_lc = NULL;
#endif
char *dft_xauthority = NULL;
char *dft_pinentry_user_data = NULL;
err = agent_simple_cmd (ctx, "RESET");
if (err)
return err;
/* Set up display, terminal and locale options. */
dft_display = getenv ("DISPLAY");
if (dft_display)
err = agent_simple_cmd (ctx, "OPTION display=%s", dft_display);
if (err)
return err;
dft_ttyname = getenv ("GPG_TTY");
if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
dft_ttyname = ttyname (0);
if (dft_ttyname)
{
err = agent_simple_cmd (ctx, "OPTION ttyname=%s", dft_ttyname);
if (err)
return err;
}
dft_ttytype = getenv ("TERM");
if (dft_ttytype)
err = agent_simple_cmd (ctx, "OPTION ttytype=%s", dft_ttytype);
if (err)
return err;
#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
old_lc = setlocale (LC_CTYPE, NULL);
if (old_lc)
{
old_lc = strdup (old_lc);
if (!old_lc)
return gpg_error_from_errno (errno);
}
dft_lc = setlocale (LC_CTYPE, "");
if (dft_lc)
err = agent_simple_cmd ("OPTION lc-ctype=%s", dft_lc);
if (old_lc)
{
setlocale (LC_CTYPE, old_lc);
free (old_lc);
}
#endif
if (err)
return err;
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
old_lc = setlocale (LC_MESSAGES, NULL);
if (old_lc)
{
old_lc = strdup (old_lc);
if (!old_lc)
err = gpg_error_from_errno (errno);
}
dft_lc = setlocale (LC_MESSAGES, "");
if (dft_lc)
err = agent_simple_cmd ("OPTION lc-messages=%s", dft_lc);
if (old_lc)
{
setlocale (LC_MESSAGES, old_lc);
free (old_lc);
}
#endif
dft_xauthority = getenv ("XAUTHORITY");
if (dft_xauthority)
err = agent_simple_cmd (ctx, "OPTION xauthority=%s", dft_xauthority);
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
err = 0;
else if (err)
return err;
dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
if (dft_pinentry_user_data)
err = agent_simple_cmd (ctx, "OPTION pinentry_user_data=%s",
dft_pinentry_user_data);
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
err = 0;
else if (err)
return err;
err = agent_simple_cmd (ctx, "OPTION allow-pinentry-notify");
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
err = 0;
else if (err)
return err;
return err;
}
/* Try to connect to the agent via socket. Handle the server's
initial greeting. */
gpg_error_t
scute_agent_initialize (void)
{
gpg_error_t err = 0;
if (agent_ctx)
{
DEBUG ("GPG Agent connection already established");
return 0;
}
err = agent_connect (&agent_ctx);
if (err)
return err;
err = agent_configure (agent_ctx);
if (err)
scute_agent_finalize ();
return err;
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static char *
unescape_status_string (const unsigned char *src)
{
char *buffer;
char *dst;
buffer = malloc (strlen (src) + 1);
if (!buffer)
return NULL;
dst = buffer;
while (*src)
{
if (*src == '%' && src[1] && src[2])
{
src++;
*dst = xtoi_2 (src);
if (*dst == '\0')
*dst = '\xff';
dst++;
src += 2;
}
else if (*src == '+')
{
*(dst++) = ' ';
src++;
}
else
*(dst++) = *(src++);
}
*dst = 0;
return buffer;
}
/* Take a 20 byte hexencoded string and put it into the the provided
20 byte buffer FPR in binary format. Returns true if successful,
and false otherwise. */
static int
unhexify_fpr (const char *hexstr, unsigned char *fpr)
{
const char *src;
int cnt;
/* Check for invalid or wrong length. */
for (src = hexstr, cnt = 0; hexdigitp (src); src++, cnt++)
;
if ((*src && !spacep (src)) || (cnt != 40))
return 0;
cnt /= 2;
for (src = hexstr, cnt = 0; *src && !spacep (src); src += 2, cnt++)
fpr[cnt] = xtoi_2 (src);
return 1;
}
/* Take the serial number from LINE and return it verbatim in a newly
allocated string. We make sure that only hex characters are
returned. */
static char *
store_serialno (const char *line)
{
const char *src;
char *ptr;
for (src = line; hexdigitp (src); src++)
;
ptr = malloc (src + 1 - line);
if (ptr)
{
memcpy (ptr, line, src - line);
ptr[src - line] = 0;
}
return ptr;
}
/* Release the card info structure INFO. */
void
scute_agent_release_card_info (struct agent_card_info_s *info)
{
if (!info)
return;
free (info->serialno);
free (info->disp_name);
free (info->disp_lang);
free (info->pubkey_url);
free (info->login_data);
memset (info, 0, sizeof (*info));
}
/* FIXME: We are not returning out of memory errors. */
static gpg_error_t
learn_status_cb (void *opaque, const char *line)
{
struct agent_card_info_s *parm = opaque;
const char *keyword = line;
int keywordlen;
int i;
for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++)
;
while (spacep (line))
line++;
if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
{
if (parm->serialno)
free (parm->serialno);
parm->serialno = store_serialno (line);
}
else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
{
if (parm->disp_name)
free (parm->disp_name);
parm->disp_name = unescape_status_string (line);
}
else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
{
if (parm->disp_lang)
free (parm->disp_lang);
parm->disp_lang = unescape_status_string (line);
}
else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
{
parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
}
else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
{
if (parm->pubkey_url)
free (parm->pubkey_url);
parm->pubkey_url = unescape_status_string (line);
}
else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
{
if (parm->login_data)
free (parm->login_data);
parm->login_data = unescape_status_string (line);
}
else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
{
parm->sig_counter = strtoul (line, NULL, 0);
}
else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
{
char *p, *buf;
buf = p = unescape_status_string (line);
if (buf)
{
while (spacep (p))
p++;
parm->chv1_cached = atoi (p);
while (*p && !spacep (p))
p++;
while (spacep (p))
p++;
for (i = 0; *p && i < 3; i++)
{
parm->chvmaxlen[i] = atoi (p);
while (*p && !spacep (p))
p++;
while (spacep (p))
p++;
}
for (i=0; *p && i < 3; i++)
{
parm->chvretry[i] = atoi (p);
while (*p && !spacep (p))
p++;
while (spacep (p))
p++;
}
free (buf);
}
}
else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
{
int no = atoi (line);
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
if (no == 1)
parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
else if (no == 2)
parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
else if (no == 3)
parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
}
else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen))
{
int no = atoi (line);
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
if (no == 1)
parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1);
else if (no == 2)
parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2);
else if (no == 3)
parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3);
}
else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
{
const char *grip = line;
while (*line && !spacep (line))
line++;
if (line - grip == 40)
{
while (spacep (line))
line++;
if (!memcmp (line, "OPENPGP.", 8))
{
int no;
line += 8;
no = atoi (line);
if (no == 1)
{
memcpy (parm->grip1, grip, 40);
parm->grip1valid = 1;
}
else if (no == 2)
{
memcpy (parm->grip2, grip, 40);
parm->grip2valid = 1;
}
else if (no == 3)
{
memcpy (parm->grip3, grip, 40);
parm->grip3valid = 1;
}
}
}
}
return 0;
}
/* Call the agent to learn about a smartcard. */
gpg_error_t
scute_agent_learn (struct agent_card_info_s *info)
{
gpg_error_t err;
memset (info, 0, sizeof (*info));
err = assuan_transact (agent_ctx, "LEARN --send",
NULL, NULL, default_inq_cb,
NULL, learn_status_cb, info);
return err;
}
static gpg_error_t
read_status_cb (void *opaque, const void *buffer, size_t length)
{
char *flag = opaque;
if (length == 0)
*flag = 'r';
else
*flag = *((char *) buffer);
return 0;
}
/* Check the agent status. This returns 0 if a token is present,
GPG_ERR_CARD_REMOVED if no token is present, and an error code
otherwise. */
gpg_error_t
scute_agent_check_status (void)
{
gpg_error_t err;
char flag = '-';
err = assuan_transact (agent_ctx, "SCD GETINFO status",
read_status_cb, &flag, default_inq_cb,
NULL, NULL, NULL);
if (err)
return err;
if (flag == 'r')
return gpg_error (GPG_ERR_CARD_REMOVED);
return 0;
}
#define MAX_SIGNATURE_LEN 256
struct signature
{
unsigned char data[MAX_SIGNATURE_LEN];
int len;
};
static gpg_error_t
pksign_cb (void *opaque, const void *buffer, size_t length)
{
struct signature *sig = opaque;
if (sig->len + length > MAX_SIGNATURE_LEN)
return gpg_error (GPG_ERR_BAD_DATA);
memcpy (&sig->data[sig->len], buffer, length);
sig->len += length;
return 0;
}
#define SIG_PREFIX "(7:sig-val(3:rsa(1:s128:"
#define SIG_PREFIX_LEN (sizeof (SIG_PREFIX) - 1)
#define SIG_POSTFIX ")))"
#define SIG_POSTFIX_LEN (sizeof (SIG_POSTFIX) - 1)
#define SIG_LEN 128
/* Call the agent to learn about a smartcard. */
gpg_error_t
scute_agent_sign (char *grip, unsigned char *data, int len,
unsigned char *sig_result, unsigned int *sig_len)
{
char cmd[150];
gpg_error_t err;
#define MAX_DATA_LEN 36
unsigned char pretty_data[2 * MAX_DATA_LEN + 1];
int i;
struct signature sig;
sig.len = 0;
if (sig_len == NULL)
return gpg_error (GPG_ERR_INV_ARG);
if (sig_result == NULL)
{
*sig_len = SIG_LEN;
return 0;
}
if (len > MAX_DATA_LEN)
return gpg_error (GPG_ERR_INV_ARG);
if (grip == NULL || sig_result == NULL || *sig_len < SIG_LEN)
return gpg_error (GPG_ERR_INV_ARG);
snprintf (cmd, sizeof (cmd), "SIGKEY %s", grip);
err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb,
NULL, NULL, NULL);
if (err)
return err;
for (i = 0; i < len; i++)
sprintf (&pretty_data[2 * i], "%02X", data[i]);
pretty_data[2 * len] = '\0';
snprintf (cmd, sizeof (cmd), "sethash --hash=tls-md5sha1 %s", pretty_data);
err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb,
NULL, NULL, NULL);
if (err)
return err;
err = assuan_transact (agent_ctx, "PKSIGN",
pksign_cb, &sig, default_inq_cb, NULL, NULL, NULL);
if (err)
return err;
if (sig.len != SIG_PREFIX_LEN + SIG_LEN + SIG_POSTFIX_LEN)
return gpg_error (GPG_ERR_BAD_SIGNATURE);
if (memcmp (sig.data, SIG_PREFIX, SIG_PREFIX_LEN))
return gpg_error (GPG_ERR_BAD_SIGNATURE);
if (memcmp (sig.data + sig.len - SIG_POSTFIX_LEN,
SIG_POSTFIX, SIG_POSTFIX_LEN))
return gpg_error (GPG_ERR_BAD_SIGNATURE);
memcpy (sig_result, sig.data + SIG_PREFIX_LEN, SIG_LEN);
*sig_len = SIG_LEN;
return 0;
}
/* Determine if FPR is trusted. */
-gpg_error_t scute_agent_is_trusted (char *fpr, bool *is_trusted)
+gpg_error_t
+scute_agent_is_trusted (char *fpr, bool *is_trusted)
{
gpg_error_t err;
bool trusted = false;
char cmd[150];
snprintf (cmd, sizeof (cmd), "ISTRUSTED %s", fpr);
err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb,
NULL, NULL, NULL);
if (err && gpg_err_code (err) != GPG_ERR_NOT_TRUSTED)
return err;
else if (!err)
trusted = true;
*is_trusted = trusted;
return 0;
}
+
+#define GET_CERT_INIT_SIZE 2048
+
+struct get_cert_s
+{
+ unsigned char *cert_der;
+ int cert_der_len;
+ int cert_der_size;
+};
+
+
+gpg_error_t
+get_cert_data_cb (void *opaque, const void *data, size_t data_len)
+{
+ struct get_cert_s *cert_s = opaque;
+ gpg_error_t err;
+ int needed_size;
+
+ needed_size = cert_s->cert_der_len + data_len;
+ if (needed_size > cert_s->cert_der_size)
+ {
+ unsigned char *new_cert_der;
+ int new_cert_der_size = cert_s->cert_der_size;
+
+ if (new_cert_der_size == 0)
+ new_cert_der_size = GET_CERT_INIT_SIZE;
+ while (new_cert_der_size < needed_size)
+ new_cert_der_size *= 2;
+
+ if (cert_s->cert_der == NULL)
+ new_cert_der = malloc (new_cert_der_size);
+ else
+ new_cert_der = realloc (cert_s->cert_der, new_cert_der_size);
+
+ if (new_cert_der == NULL)
+ return gpg_error_from_syserror ();
+
+ cert_s->cert_der = new_cert_der;
+ cert_s->cert_der_size = new_cert_der_size;
+ }
+
+ memcpy (cert_s->cert_der + cert_s->cert_der_len, data, data_len);
+ cert_s->cert_der_len += data_len;
+
+ return 0;
+}
+
+
+/* Try to get certificate for key numer NO. */
+gpg_error_t
+scute_agent_get_cert (int no, struct cert *cert)
+{
+ gpg_error_t err;
+ char cmd[150];
+ struct get_cert_s cert_s;
+
+ cert_s.cert_der = NULL;
+ cert_s.cert_der_len = 0;
+ cert_s.cert_der_size = 0;
+
+ snprintf (cmd, sizeof (cmd), "SCD READCERT OPENPGP.%i", no);
+ err = assuan_transact (agent_ctx, cmd, get_cert_data_cb, &cert_s,
+ NULL, NULL, NULL, NULL);
+ /* Just to be safe... */
+ if (!err && cert_s.cert_der_len <= 16)
+ err = gpg_error (GPG_ERR_BAD_CERT);
+ if (err)
+ {
+ if (cert_s.cert_der)
+ free (cert_s.cert_der);
+ return err;
+ }
+
+ cert->cert_der = cert_s.cert_der;
+ cert->cert_der_len = cert_s.cert_der_len;
+
+ return 0;
+}
+
void
scute_agent_finalize (void)
{
if (!agent_ctx)
{
DEBUG ("no GPG Agent connection established");
return;
}
assuan_disconnect (agent_ctx);
agent_ctx = NULL;
}
diff --git a/src/cert-object.c b/src/cert-object.c
index b3befb0..26c1b90 100644
--- a/src/cert-object.c
+++ b/src/cert-object.c
@@ -1,617 +1,658 @@
/* cert-object.c - Convert a GPGSM certificate into a PKCS #11 object.
Copyright (C) 2006, 2007 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <gpg-error.h>
#include "cryptoki.h"
#include "support.h"
#include "cert.h"
#define atoi_1(p) (*(p) - '0' )
#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
static bool
time_to_ck_date (time_t *atime, CK_DATE *ckdate)
{
struct tm broken_time;
int nr;
if (!*atime)
return false;
#ifdef HAVE_LOCALTIME_R
if (!localtime_r (atime, &broken_time))
return false;
#else
{
/* FIXME: This is not thread-safe, but it minimizes risk. */
struct tm *b_time = localtime (atime);
if (!b_time)
return false;
memcpy (&broken_time, b_time, sizeof (*b_time));
}
#endif
/* We can only represent years until 9999. */
if (!(broken_time.tm_year >= 0 && broken_time.tm_year <= 8099
&& broken_time.tm_mon >= 0 && broken_time.tm_mon <= 11
&& broken_time.tm_mday >= 1 && broken_time.tm_mday <= 31))
return false;
#define LAST_DIGIT(d) (((d) % 10) + '0')
nr = broken_time.tm_year + 1900;
ckdate->year[3] = LAST_DIGIT (nr);
nr = nr / 10;
ckdate->year[2] = LAST_DIGIT (nr);
nr = nr / 10;
ckdate->year[1] = LAST_DIGIT (nr);
nr = nr / 10;
ckdate->year[0] = LAST_DIGIT (nr);
nr = broken_time.tm_mon + 1;
ckdate->month[1] = LAST_DIGIT (nr);
nr = nr / 10;
ckdate->month[0] = LAST_DIGIT (nr);
nr = broken_time.tm_mday;
ckdate->day[1] = LAST_DIGIT (nr);
nr = nr / 10;
ckdate->day[0] = LAST_DIGIT (nr);
return true;
}
static gpg_error_t
asn1_get_len (unsigned char **asn1, int *asn1_len, int *rlen)
{
unsigned char *ptr = *asn1;
int len = *asn1_len;
int cnt;
int result = 0;
if (len < 1)
return gpg_error (GPG_ERR_GENERAL);
if (*ptr & 0x80)
{
cnt = *ptr & 0x7f;
ptr++;
len--;
}
else
cnt = 1;
/* We only support a limited number of length bytes. */
if (cnt > 2 || len < cnt)
return gpg_error (GPG_ERR_GENERAL);
while (cnt--)
{
result = (result << 8) | *ptr;
ptr++;
len--;
}
*asn1 = ptr;
*asn1_len = len;
*rlen = result;
return 0;
}
/* A path to an ASN.1 element that can be looked up with
asn1_get_element. The last element in the list is returned (that
one should have ENTER being false. */
struct asn1_path
{
unsigned char tag;
/* True if we should enter the element, false if we should skip
it. */
bool enter;
};
static gpg_error_t
asn1_get_element (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len,
struct asn1_path *path, int path_size)
{
gpg_error_t err;
unsigned char *prev_certp = NULL;
unsigned char *certp = cert;
int cert_left = cert_len;
int len;
int i;
for (i = 0; i < path_size; i++)
{
prev_certp = certp;
if (cert_left < 1 || *certp != path[i].tag)
return gpg_error (GPG_ERR_GENERAL);
certp++;
cert_left--;
err = asn1_get_len (&certp, &cert_left, &len);
if (err)
return err;
if (!path[i].enter)
{
if (cert_left < len)
return gpg_error (GPG_ERR_GENERAL);
certp += len;
cert_left -= len;
}
else
{
/* Special code to deal with ASN.1 data encapsulated in a
bit string. */
if (path[i].tag == '\x03')
{
if (cert_left < 1 || *certp != '\x00')
return gpg_error (GPG_ERR_GENERAL);
certp++;
cert_left--;
}
}
}
/* We found the subject. */
*sub_start = prev_certp;
*sub_len = certp - prev_certp;
return 0;
}
static gpg_error_t
asn1_get_issuer (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len)
{
/* The path to the issuer entry in the DER file. This is
Sequence->Sequence->Version,Serial,AlgID,Issuer. */
struct asn1_path path[] = { { '\x30', true }, { '\x30', true },
{ '\xa0', false }, { '\x02', false },
{ '\x30', false }, { '\x30', false } };
return asn1_get_element (cert, cert_len, sub_start, sub_len,
path, DIM (path));
}
static gpg_error_t
asn1_get_subject (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len)
{
/* The path to the subject entry in the DER file. This is
Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject. */
struct asn1_path path[] = { { '\x30', true }, { '\x30', true },
{ '\xa0', false }, { '\x02', false },
{ '\x30', false }, { '\x30', false },
{ '\x30', false }, { '\x30', false } };
return asn1_get_element (cert, cert_len, sub_start, sub_len,
path, DIM (path));
}
static gpg_error_t
asn1_get_serial (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len)
{
/* The path to the serial entry in the DER file. This is
Sequence->Sequence->Version,Serial. */
struct asn1_path path[] = { { '\x30', true }, { '\x30', true },
{ '\xa0', false }, { '\x02', false } };
return asn1_get_element (cert, cert_len, sub_start, sub_len,
path, DIM (path));
}
static gpg_error_t
asn1_get_modulus (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len)
{
gpg_error_t err;
int len;
struct asn1_path path[] = { { '\x30', true }, { '\x30', true },
{ '\xa0', false }, { '\x02', false },
{ '\x30', false }, { '\x30', false },
{ '\x30', false }, { '\x30', false },
{ '\x30', true }, { '\x30', false },
{ '\x03', true }, { '\x30', true },
{ '\x02', false } };
/* The path to the modulus entry in the DER file. This is
Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject,
Sequence->Sequence,Bitstring->Sequence->Integer,Integer */
err = asn1_get_element (cert, cert_len, sub_start, sub_len,
path, DIM (path));
if (err)
return err;
if (*sub_len < 1)
return gpg_error (GPG_ERR_GENERAL);
(*sub_start)++;
(*sub_len)--;
err = asn1_get_len (sub_start, sub_len, &len);
if (err)
return err;
/* PKCS #11 expects an unsigned big integer. */
while (**sub_start == '\x00' && *sub_len > 0)
{
(*sub_start)++;
(*sub_len)--;
}
return 0;
}
static gpg_error_t
asn1_get_public_exp (unsigned char *cert, int cert_len,
unsigned char **sub_start, int *sub_len)
{
gpg_error_t err;
int len;
/* The path to the public exp entry in the DER file. This is
Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject,
Sequence->Sequence,Bitstring->Sequence->Integer,Integer */
struct asn1_path path[] = { { '\x30', true }, { '\x30', true },
{ '\xa0', false }, { '\x02', false },
{ '\x30', false }, { '\x30', false },
{ '\x30', false }, { '\x30', false },
{ '\x30', true }, { '\x30', false },
{ '\x03', true }, { '\x30', true },
{ '\x02', false }, { '\x02', false } };
err = asn1_get_element (cert, cert_len, sub_start, sub_len,
path, DIM (path));
if (err)
return err;
if (*sub_len < 1)
return gpg_error (GPG_ERR_GENERAL);
(*sub_start)++;
(*sub_len)--;
err = asn1_get_len (sub_start, sub_len, &len);
if (err)
return err;
/* PKCS #11 expects an unsigned big integer. */
while (**sub_start == '\x00' && *sub_len > 0)
{
(*sub_start)++;
(*sub_len)--;
}
return 0;
}
static gpg_error_t
attr_one (CK_ATTRIBUTE_PTR attr, CK_ULONG *attr_count,
CK_ATTRIBUTE_TYPE type, CK_VOID_PTR val, CK_ULONG size)
{
CK_ULONG i = *attr_count;
attr[i].type = type;
attr[i].ulValueLen = size;
attr[i].pValue = malloc (size);
if (attr[i].pValue == NULL)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (attr[i].pValue, val, size);
(*attr_count)++;
return 0;
}
static gpg_error_t
attr_empty (CK_ATTRIBUTE_PTR attr, CK_ULONG *attr_count,
CK_ATTRIBUTE_TYPE type)
{
CK_ULONG i = *attr_count;
attr[i].type = type;
attr[i].ulValueLen = 0;
attr[i].pValue = NULL_PTR;
(*attr_count)++;
return 0;
}
void
scute_attr_free (CK_ATTRIBUTE_PTR attr, CK_ULONG attr_count)
{
while (0 < attr_count--)
free (attr[attr_count].pValue);
}
gpg_error_t
scute_attr_cert (struct cert *cert,
CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp)
{
CK_RV err;
CK_ATTRIBUTE_PTR attr;
CK_ULONG attr_count;
unsigned char *subject_start;
int subject_len;
unsigned char *issuer_start;
int issuer_len;
unsigned char *serial_start;
int serial_len;
CK_OBJECT_CLASS obj_class = CKO_CERTIFICATE;
CK_BBOOL obj_token = CK_TRUE;
CK_BBOOL obj_private = CK_FALSE;
CK_BBOOL obj_modifiable = CK_FALSE;
CK_BYTE obj_label[] = { 'D', 'u', 'm', 'm', 'y', ' ',
'L', 'a', 'b', 'e', 'l' };
CK_CERTIFICATE_TYPE obj_cert_type = CKC_X_509;
CK_BBOOL obj_trusted = cert->is_trusted;
CK_ULONG obj_cert_cat = 0;
CK_BYTE obj_check_value[3] = { '\0', '\0', '\0' };
CK_DATE obj_start_date;
CK_DATE obj_end_date;
CK_ULONG obj_java_midp_sec_domain = 0;
err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
&subject_start, &subject_len);
if (!err)
err = asn1_get_issuer (cert->cert_der, cert->cert_der_len,
&issuer_start, &issuer_len);
if (!err)
err = asn1_get_serial (cert->cert_der, cert->cert_der_len,
&serial_start, &serial_len);
if (err)
return err;
#define NR_ATTR_CERT 20
attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_CERT);
attr_count = 0;
if (!attr)
return gpg_error (GPG_ERR_ENOMEM);
#define one_attr_ext(type, val, size) \
if (!err) \
err = attr_one (attr, &attr_count, type, val, size)
#define one_attr(type, val) one_attr_ext (type, &val, sizeof (val))
#define empty_attr(type) \
if (!err) \
err = attr_empty (attr, &attr_count, type)
one_attr (CKA_CLASS, obj_class);
one_attr (CKA_TOKEN, obj_token);
one_attr (CKA_PRIVATE, obj_private);
one_attr (CKA_MODIFIABLE, obj_modifiable);
one_attr (CKA_LABEL, obj_label);
one_attr (CKA_CERTIFICATE_TYPE, obj_cert_type);
one_attr (CKA_TRUSTED, obj_trusted);
one_attr (CKA_CERTIFICATE_CATEGORY, obj_cert_cat);
/* FIXME: Calculate check_value. */
one_attr (CKA_CHECK_VALUE, obj_check_value);
+#if 0
if (time_to_ck_date (&cert->timestamp, &obj_start_date))
{
one_attr (CKA_START_DATE, obj_start_date);
}
else
{
empty_attr (CKA_START_DATE);
}
if (time_to_ck_date (&cert->expires, &obj_end_date))
{
one_attr (CKA_END_DATE, obj_end_date);
}
else
{
empty_attr (CKA_END_DATE);
}
+#else
+ /* For now, we disable these fields. We can parse them from the
+ certificate just as the other data. However, we would like to
+ avoid parsing the certificates at all, let's see how much
+ functionality we really need in the PKCS#11 token first. */
+ empty_attr (CKA_START_DATE);
+ empty_attr (CKA_END_DATE);
+#endif
one_attr_ext (CKA_SUBJECT, subject_start, subject_len);
+#if 0
+ /* If we get the info directly from the card, we don't have a
+ fingerprint, and parsing the subject key identifier is quite a
+ mouth full. Let's try a different approach for now. */
one_attr_ext (CKA_ID, cert->fpr, 40);
+#else
+ {
+ char certptr[40];
+ snprintf (certptr, DIM (certptr), "%p", cert);
+ one_attr_ext (CKA_ID, certptr, strlen (certptr));
+ }
+#endif
+
one_attr_ext (CKA_ISSUER, issuer_start, issuer_len);
one_attr_ext (CKA_SERIAL_NUMBER, serial_start, serial_len);
one_attr_ext (CKA_VALUE, cert->cert_der, cert->cert_der_len);
empty_attr (CKA_URL);
empty_attr (CKA_HASH_OF_SUBJECT_PUBLIC_KEY);
empty_attr (CKA_HASH_OF_ISSUER_PUBLIC_KEY);
one_attr (CKA_JAVA_MIDP_SECURITY_DOMAIN, obj_java_midp_sec_domain);
if (err)
{
scute_attr_free (attr, attr_count);
return err;
}
/* FIXME: Not completely safe. */
assert (NR_ATTR_CERT == attr_count);
*attrp = attr;
*attr_countp = attr_count;
return 0;
}
gpg_error_t
scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
CK_ULONG *attr_countp)
{
CK_RV err;
CK_ATTRIBUTE_PTR attr;
CK_ULONG attr_count;
unsigned char *subject_start;
int subject_len;
unsigned char *modulus_start;
int modulus_len;
unsigned char *public_exp_start;
int public_exp_len;
CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY;
CK_BBOOL obj_token = CK_TRUE;
CK_BBOOL obj_private = CK_FALSE;
CK_BBOOL obj_modifiable = CK_FALSE;
CK_BYTE obj_label[] = { 'O', 'P', 'E', 'N', 'P', 'G',
'P', '.', '3' };
CK_KEY_TYPE obj_key_type = CKK_RSA;
CK_DATE obj_start_date;
CK_DATE obj_end_date;
CK_BBOOL obj_derive = CK_FALSE;
CK_BBOOL obj_local = CK_FALSE; /* FIXME: Unknown. */
CK_MECHANISM_TYPE obj_key_gen = CKM_RSA_PKCS_KEY_PAIR_GEN;
CK_MECHANISM_TYPE obj_mechanisms[] = { CKM_RSA_PKCS };
CK_BBOOL obj_sensitive = CK_TRUE;
CK_BBOOL obj_decrypt = CK_FALSE; /* Authentication only for now. */
CK_BBOOL obj_sign = CK_TRUE;
CK_BBOOL obj_sign_recover = CK_FALSE;
CK_BBOOL obj_unwrap = CK_FALSE;
CK_BBOOL obj_extractable = CK_FALSE;
CK_BBOOL obj_always_sensitive = CK_TRUE;
CK_BBOOL obj_never_extractable = CK_TRUE;
CK_BBOOL obj_wrap_with_trusted = CK_FALSE;
CK_BBOOL obj_always_authenticate = CK_FALSE;
err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
&subject_start, &subject_len);
if (!err)
err = asn1_get_modulus (cert->cert_der, cert->cert_der_len,
&modulus_start, &modulus_len);
if (!err)
err = asn1_get_public_exp (cert->cert_der, cert->cert_der_len,
&public_exp_start, &public_exp_len);
if (err)
return err;
#define NR_ATTR_PRV 27
attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_PRV);
attr_count = 0;
if (!attr)
return gpg_error (GPG_ERR_ENOMEM);
#undef one_attr_ext
#define one_attr_ext(type, val, size) \
if (!err) \
err = attr_one (attr, &attr_count, type, val, size)
#undef one_attr
#define one_attr(type, val) one_attr_ext (type, &val, sizeof (val))
#undef empty_attr
#define empty_attr(type) \
if (!err) \
err = attr_empty (attr, &attr_count, type)
one_attr (CKA_CLASS, obj_class);
one_attr (CKA_TOKEN, obj_token);
one_attr (CKA_PRIVATE, obj_private);
one_attr (CKA_MODIFIABLE, obj_modifiable);
one_attr (CKA_LABEL, obj_label);
one_attr (CKA_KEY_TYPE, obj_key_type);
+#if 0
+ /* If we get the info directly from the card, we don't have a
+ fingerprint, and parsing the subject key identifier is quite a
+ mouth full. Let's try a different approach for now. */
one_attr_ext (CKA_ID, cert->fpr, 40);
+#else
+ {
+ char certptr[40];
+ snprintf (certptr, DIM (certptr), "%p", cert);
+ one_attr_ext (CKA_ID, certptr, strlen (certptr));
+ }
+#endif
+#if 0
if (time_to_ck_date (&cert->timestamp, &obj_start_date))
{
one_attr (CKA_START_DATE, obj_start_date);
}
else
{
empty_attr (CKA_START_DATE);
}
if (time_to_ck_date (&cert->expires, &obj_end_date))
{
one_attr (CKA_END_DATE, obj_end_date);
}
else
{
empty_attr (CKA_END_DATE);
}
+#else
+ /* For now, we disable these fields. We can parse them from the
+ certificate just as the other data. However, we would like to
+ avoid parsing the certificates at all, let's see how much
+ functionality we really need in the PKCS#11 token first. */
+ empty_attr (CKA_START_DATE);
+ empty_attr (CKA_END_DATE);
+#endif
one_attr (CKA_DERIVE, obj_derive);
one_attr (CKA_LOCAL, obj_local);
one_attr (CKA_KEY_GEN_MECHANISM, obj_key_gen);
one_attr (CKA_ALLOWED_MECHANISMS, obj_mechanisms);
one_attr_ext (CKA_SUBJECT, subject_start, subject_len);
one_attr (CKA_SENSITIVE, obj_sensitive);
one_attr (CKA_DECRYPT, obj_decrypt);
one_attr (CKA_SIGN, obj_sign);
one_attr (CKA_SIGN_RECOVER, obj_sign_recover);
one_attr (CKA_UNWRAP, obj_unwrap);
one_attr (CKA_EXTRACTABLE, obj_extractable);
one_attr (CKA_ALWAYS_SENSITIVE, obj_always_sensitive);
one_attr (CKA_NEVER_EXTRACTABLE, obj_never_extractable);
one_attr (CKA_WRAP_WITH_TRUSTED, obj_wrap_with_trusted);
empty_attr (CKA_UNWRAP_TEMPLATE);
one_attr (CKA_ALWAYS_AUTHENTICATE, obj_always_authenticate);
one_attr_ext (CKA_MODULUS, modulus_start, modulus_len);
one_attr_ext (CKA_PUBLIC_EXPONENT, public_exp_start, public_exp_len);
if (err)
{
scute_attr_free (attr, attr_count);
return err;
}
/* FIXME: Not completely safe. */
assert (NR_ATTR_PRV == attr_count);
*attrp = attr;
*attr_countp = attr_count;
return 0;
}
diff --git a/src/cert.h b/src/cert.h
index 9fa750d..eb09802 100644
--- a/src/cert.h
+++ b/src/cert.h
@@ -1,124 +1,136 @@
/* cert.h - Scute certificate management.
Copyright (C) 2006, 2007 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#ifndef CERT_H
#define CERT_H 1
#include <stdbool.h>
#include <time.h>
#include <gpg-error.h>
#include <assuan.h>
#include "cryptoki.h"
/* A certificate structure holds all information of a certificate
during a certificate search. */
struct cert
{
/* True if we started to fill in a certificate. */
bool valid;
+#if 1
+ /* We disable some elements, because they are easy to get from gpgsm
+ but hard to get from the card directly. These fields are only
+ valid when getting the certificate through gpgsm, so don't use
+ them. */
+
/* The key length. */
int length;
/* The public key algorithm. */
int pubkey_algo;
/* The key ID. */
unsigned char keyid[17];
- /* The timestamp. */
- time_t timestamp;
-
- /* The expiration time. */
- time_t expires;
-
/* The X.509 serial number. */
char *issuer_serial;
/* The X.509 issuer name. */
char *issuer_name;
/* The user ID strings. */
char *uid;
+ /* The timestamp. */
+ time_t timestamp;
+
+ /* The expiration time. */
+ time_t expires;
+#endif
+
+ /* The following entries are required to create a PKCS #11
+ certificate (in cert-object.c). GpgSM delivers them directly, if
+ we get the cert from the card, we need to read them from the cert
+ ourselves. */
+
/* The fingerprint. */
unsigned char fpr[41];
/* The key grip. */
unsigned char grip[41];
/* The chain ID. */
unsigned char chain_id[41];
/* The certificate in DER format. This is not entered by the search
function, but afterwards by the filter before converting it into
a PKCS #11 object. */
unsigned char *cert_der;
int cert_der_len;
/* If the certificate is trusted or not. For performance reasons,
this is not entered by the search function, but afterwards by the
filter before converting it into a PKCS #11 object. */
bool is_trusted;
};
/* From cert-gpgsm.c. */
/* The callback type invoked for each certificate found in the
search. */
typedef gpg_error_t (*cert_search_cb_t) (void *hook, struct cert *cert);
/* Invoke SEARCH_CB for each certificate found using assuan connection
CTX to GPGSM. */
gpg_error_t scute_gpgsm_search_certs_by_grip (const char *grip,
cert_search_cb_t search_cb,
void *search_cb_hook);
/* Invoke SEARCH_CB for each certificate found using assuan connection
CTX to GPGSM. */
gpg_error_t scute_gpgsm_search_certs_by_fpr (const char *fpr,
cert_search_cb_t search_cb,
void *search_cb_hook);
/* From cert-object.c. */
gpg_error_t scute_attr_cert (struct cert *cert,
CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp);
gpg_error_t scute_attr_prv (struct cert *cert, CK_ATTRIBUTE_PTR *attrp,
CK_ULONG *attr_countp);
void scute_attr_free (CK_ATTRIBUTE_PTR attr, CK_ULONG attr_count);
#endif /* !CERT_H */
diff --git a/src/gpgsm.c b/src/gpgsm.c
index 148265b..4bfea51 100644
--- a/src/gpgsm.c
+++ b/src/gpgsm.c
@@ -1,125 +1,149 @@
/* gpgsm.c - Talking to gpgsm.
- Copyright (C) 2006 g10 Code GmbH
+ Copyright (C) 2006, 2008 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <locale.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <assuan.h>
#include <gpg-error.h>
#include "cryptoki.h"
#include "support.h"
#include "cert.h"
+#include "agent.h"
#include "gpgsm.h"
struct search
{
bool found;
cert_get_cb_t cert_get_cb;
void *hook;
};
static gpg_error_t
search_cb (void *hook, struct cert *cert)
{
struct search *ctx = hook;
gpg_error_t err = 0;
CK_ATTRIBUTE_PTR attrp;
CK_ULONG attr_countp;
/* Add the private key object only once. */
if (!ctx->found)
{
err = scute_attr_prv (cert, &attrp, &attr_countp);
if (err)
return err;
err = (*ctx->cert_get_cb) (ctx->hook, attrp, attr_countp);
if (err)
{
scute_attr_free (attrp, attr_countp);
return err;
}
ctx->found = true;
}
/* Add the certificate chain recursively before adding the
certificate. But ignore errors. If the chain is incomplete, we
might still be able to proceed, for example with client
authentication. */
if (strcmp (cert->chain_id, cert->fpr))
err = scute_gpgsm_search_certs_by_fpr (cert->chain_id, search_cb, ctx);
/* Turn this certificate into a certificate object. */
err = scute_attr_cert (cert, &attrp, &attr_countp);
if (err)
return err;
err = (*ctx->cert_get_cb) (ctx->hook, attrp, attr_countp);
if (err)
{
scute_attr_free (attrp, attr_countp);
return err;
}
return err;
}
/* Create the attributes required for a new certificate object.
Returns allocated attributes for the certificate object in ATTRP
and ATTR_COUNTP, and for the private key object in PRV_ATTRP
and PRV_ATTR_COUNTP. */
gpg_error_t
-scute_gpgsm_get_cert (char *grip, cert_get_cb_t cert_get_cb, void *hook)
+scute_gpgsm_get_cert (char *grip, int no, cert_get_cb_t cert_get_cb, void *hook)
{
gpg_error_t err;
struct search search;
search.found = false;
search.cert_get_cb = cert_get_cb;
search.hook = hook;
+ /* If the key is from the card, we might get the certificate from
+ the card as well. */
+ if (no >= 0)
+ {
+ struct cert cert;
+
+ memset (&cert, '\0', sizeof (cert));
+ err = scute_agent_get_cert (no, &cert);
+ if (! err)
+ {
+#if 0
+ /* For now, we don't need no stinking chain. */
+
+ /* As we only have the DER certificate from the card, we need to
+ parse that and fill out the missing info and try to get the
+ certificate chain from gpgsm. */
+ err = scute_cert_from_der (&cert);
+ if (! err)
+ err = search_cb (hook, &cert);
+#endif
+ return err;
+ }
+ }
+
err = scute_gpgsm_search_certs_by_grip (grip, search_cb, &search);
-
return err;
}
diff --git a/src/gpgsm.h b/src/gpgsm.h
index 71e27cb..963fdd4 100644
--- a/src/gpgsm.h
+++ b/src/gpgsm.h
@@ -1,53 +1,53 @@
/* gpgsm.h - Interface for talking to gpgsm.
Copyright (C) 2006 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#ifndef GPGSM_H
#define GPGSM_H 1
#include <gpg-error.h>
#include "cryptoki.h"
#include "table.h"
/* The callback type invoked for each certificate found in the
search. */
typedef gpg_error_t (*cert_get_cb_t) (void *hook,
CK_ATTRIBUTE_PTR attrp,
CK_ULONG attr_countp);
/* Create the attributes required for a new certificate object.
Returns allocated attributes for the certificate object in ATTRP
and ATTR_COUNTP, and for the private key object in PRV_ATTRP
and PRV_ATTR_COUNTP. */
-gpg_error_t scute_gpgsm_get_cert (char *grip, cert_get_cb_t cert_get_cb,
- void *hook);
+gpg_error_t scute_gpgsm_get_cert (char *grip, int no,
+ cert_get_cb_t cert_get_cb, void *hook);
#endif /* GPGSM_H */
diff --git a/src/slots.c b/src/slots.c
index 29923db..7ecb99e 100644
--- a/src/slots.c
+++ b/src/slots.c
@@ -1,1013 +1,1013 @@
/* slots.c - Slot management.
Copyright (C) 2006 g10 Code GmbH
This file is part of Scute.
Scute 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 2 of the License, or
(at your option) any later version.
Scute 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 Scute; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
In addition, as a special exception, g10 Code GmbH gives permission
to link this library: with the Mozilla Foundation's code for
Mozilla (or with modified versions of it that use the same license
as the "Mozilla" code), and distribute the linked executables. You
must obey the GNU General Public License in all respects for all of
the code used other than "Mozilla". If you modify this file, you
may extend this exception to your version of the file, but you are
not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "cryptoki.h"
#include "table.h"
#include "error-mapping.h"
#include "slots.h"
#include "agent.h"
#include "support.h"
#include "gpgsm.h"
#include "debug.h"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* A session is just a slot identifier with a per-slot session
identifier. */
/* Must be power of two. */
#define SLOT_MAX (1 << 15)
#define SESSION_SLOT_MASK (SLOT_MAX - 1)
#define SESSION_SLOT_SHIFT 16
#define SESSION_MAX (1 << SESSION_SLOT_SHIFT)
#define SESSION_ID_MASK (SESSION_MAX - 1)
/* Get slot ID from session. */
#define SESSION_SLOT(session) \
((session >> SESSION_SLOT_SHIFT) & SESSION_SLOT_MASK)
/* Get session ID from session. */
#define SESSION_ID(session) (session & SESSION_ID_MASK)
/* Because the slot is already 1-based, we can make the session 0-based. */
#define SESSION_BUILD_ID(slot, session) \
(((slot & SESSION_SLOT_MASK) << SESSION_SLOT_SHIFT) \
| (session & SESSION_ID_MASK))
/* We use one-based IDs. */
#define OBJECT_ID_TO_IDX(id) (id - 1)
#define OBJECT_IDX_TO_ID(idx) (idx + 1)
struct object
{
CK_ATTRIBUTE_PTR attributes;
CK_ULONG attributes_count;
};
/* A mechanism. */
struct mechanism
{
CK_MECHANISM_TYPE type;
CK_MECHANISM_INFO info;
};
/* We use one-based IDs. */
#define MECHANISM_ID_TO_IDX(id) (id - 1)
#define MECHANISM_IDX_TO_ID(idx) (idx + 1)
/* The session state. */
struct session
{
/* True iff read-write session. */
bool rw;
/* The list of objects for the current search. */
object_iterator_t *search_result;
/* The length of the list of objects for the current search. */
int search_result_len;
/* The signing key. */
CK_OBJECT_HANDLE signing_key;
};
/* The slot status. */
typedef enum
{
SLOT_STATUS_USED = 0,
SLOT_STATUS_DEAD = 1
} slot_status_t;
struct slot
{
/* The slot status. Starts out as 0 (pristine). */
slot_status_t status;
/* The slot login status. Starts out as 0 (public). */
slot_login_t login;
/* True iff a token is present. */
bool token_present;
/* The supported mechanisms. */
scute_table_t mechanisms;
/* The sessions. */
scute_table_t sessions;
/* The objects on the token. */
scute_table_t objects;
/* The info about the current token. */
struct agent_card_info_s info;
};
/* The slot table. */
static scute_table_t slots;
/* Deallocator for mechanisms. */
static void
mechanism_dealloc (void *data)
{
free (data);
}
/* Allocator for mechanisms. The hook must be a pointer to a CK_FLAGS
that should be a combination of CKF_SIGN and/or CKF_DECRYPT. */
static gpg_error_t
mechanism_alloc (void **data_r, void *hook)
{
struct mechanism *mechanism;
CK_FLAGS *flags = hook;
mechanism = calloc (1, sizeof (*mechanism));
if (mechanism == NULL)
return gpg_error_from_syserror ();
/* Set some default values. */
mechanism->type = CKM_RSA_PKCS;
mechanism->info.ulMinKeySize = 1024;
mechanism->info.ulMaxKeySize = 1024;
mechanism->info.flags = CKF_HW | (*flags);
*data_r = mechanism;
return 0;
}
static void
object_dealloc (void *data)
{
struct object *obj = data;
while (0 < obj->attributes_count--)
free (obj->attributes[obj->attributes_count].pValue);
free (obj->attributes);
free (obj);
}
/* Allocator for objects. The hook is currently unused. */
static gpg_error_t
object_alloc (void **data_r, void *hook)
{
struct object *object;
object = calloc (1, sizeof (*object));
if (object == NULL)
return gpg_error_from_syserror ();
*data_r = object;
return 0;
}
static void
session_dealloc (void *data)
{
struct session *session = data;
if (session->search_result)
free (session->search_result);
free (session);
}
/* Allocator for sessions. The hook is currently unused. */
static gpg_error_t
session_alloc (void **data_r, void *hook)
{
struct session *session;
session = calloc (1, sizeof (*session));
if (session == NULL)
return gpg_error_from_syserror ();
*data_r = session;
return 0;
}
/* Deallocator for slots. */
static void
slot_dealloc (void *data)
{
struct slot *slot = data;
scute_table_destroy (slot->sessions);
scute_table_destroy (slot->mechanisms);
scute_table_destroy (slot->objects);
free (slot);
}
/* Allocator for slots. The hook does not indicate anything at this
point. */
static gpg_error_t
slot_alloc (void **data_r, void *hook)
{
gpg_error_t err;
struct slot *slot;
int idx;
CK_FLAGS flags;
slot = calloc (1, sizeof (*slot));
if (slot == NULL)
return gpg_error_from_syserror ();
err = scute_table_create (&slot->mechanisms, mechanism_alloc,
mechanism_dealloc);
if (err)
goto slot_alloc_out;
/* Register the signing mechanism. */
flags = CKF_SIGN;
err = scute_table_alloc (slot->mechanisms, &idx, NULL, &flags);
if (err)
goto slot_alloc_out;
err = scute_table_create (&slot->sessions, session_alloc, session_dealloc);
if (err)
goto slot_alloc_out;
err = scute_table_create (&slot->objects, object_alloc, object_dealloc);
if (err)
goto slot_alloc_out;
slot->status = SLOT_STATUS_USED;
slot->token_present = false;
slot->login = SLOT_LOGIN_PUBLIC;
*data_r = slot;
slot_alloc_out:
if (err)
slot_dealloc (slot);
return err;
}
/* Initialize the slot list. */
CK_RV
scute_slots_initialize (void)
{
gpg_error_t err;
int slot_idx;
err = scute_table_create (&slots, slot_alloc, slot_dealloc);
if (err)
return err;
/* Allocate a new slot for authentication. */
err = scute_table_alloc (slots, &slot_idx, NULL, NULL);
if (err)
scute_slots_finalize ();
/* FIXME: Allocate a new slot for signing and decryption of
email. */
return scute_gpg_err_to_ck (err);
}
void
scute_slots_finalize (void)
{
if (slots == NULL)
return;
/* This recursively releases all slots and any objects associated
with them. */
scute_table_destroy (slots);
slots = NULL;
}
/* Reset the slot SLOT after the token has been removed. */
static void
slot_reset (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
int oid;
/* This also resets the login state. */
slot_close_all_sessions (id);
oid = scute_table_first (slot->objects);
while (!scute_table_last (slot->objects, oid))
scute_table_dealloc (slot->objects, &oid);
assert (scute_table_used (slot->objects) == 0);
scute_agent_release_card_info (&slot->info);
slot->token_present = false;
}
static gpg_error_t
add_object (void *hook, CK_ATTRIBUTE_PTR attrp,
CK_ULONG attr_countp)
{
gpg_error_t err;
struct slot *slot = hook;
struct object *object;
unsigned int oidx;
void *objp;
err = scute_table_alloc (slot->objects, &oidx, &objp, NULL);
if (err)
return err;
object = objp;
object->attributes = attrp;
object->attributes_count = attr_countp;
return 0;
}
/* Initialize the slot after a token has been inserted. SLOT->info
must already be valid. */
static gpg_error_t
slot_init (slot_iterator_t id)
{
gpg_error_t err = 0;
struct slot *slot = scute_table_data (slots, id);
- err = scute_gpgsm_get_cert (slot->info.grip3, add_object, slot);
+ err = scute_gpgsm_get_cert (slot->info.grip3, 3, add_object, slot);
if (err)
goto init_out;
/* FIXME: Perform the rest of the initialization of the
token. */
slot->token_present = true;
init_out:
if (err)
slot_reset (id);
return err;
}
/* Update the slot SLOT. */
CK_RV
slots_update_slot (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
gpg_error_t err;
if (slot->token_present)
{
err = scute_agent_check_status ();
if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED)
slot_reset (id);
else if (err)
return scute_gpg_err_to_ck (err);
else
return 0;
}
/* At this point, the card was or is removed, and we need to reopen
the session, if possible. */
err = scute_agent_learn (&slot->info);
/* First check if this is really an OpenPGP card. FIXME: Should
probably report the error in a better way. */
if (!err && (!slot->info.serialno
|| strncmp (slot->info.serialno, "D27600012401", 12)
|| strlen (slot->info.serialno) != 32))
{
DEBUG ("token not an OpenPGP card: %s", slot->info.serialno);
err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
scute_agent_release_card_info (&slot->info);
}
if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED
|| gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
/* Nothing to do. */
err = 0;
else if (err == 0)
err = slot_init (id);
return scute_sys_to_ck (err);
}
/* Update the slot list by finding new devices. Please note that
Mozilla NSS currently assumes that the slot list never shrinks (see
TODO file for a discussion). This is the only function allowed to
manipulate the slot list. */
CK_RV
slots_update (void)
{
slot_iterator_t id = scute_table_first (slots);
while (!scute_table_last (slots, id))
{
CK_RV err;
err = slots_update_slot (id);
if (err)
return err;
id = scute_table_next (slots, id);
}
return CKR_OK;
}
/* Begin iterating over the list of slots. */
CK_RV
slots_iterate_first (slot_iterator_t *slot)
{
*slot = scute_table_first (slots);
return CKR_OK;
}
/* Continue iterating over the list of slots. */
CK_RV
slots_iterate_next (slot_iterator_t *slot)
{
*slot = scute_table_next (slots, *slot);
return CKR_OK;
}
/* Return true iff the previous slot was the last one. */
bool
slots_iterate_last (slot_iterator_t *slot)
{
return scute_table_last (slots, *slot);
}
/* Acquire the slot for the slot ID ID. */
CK_RV
slots_lookup (CK_SLOT_ID id, slot_iterator_t *id_r)
{
struct slot *slot = scute_table_data (slots, id);
if (slot == NULL)
return CKR_SLOT_ID_INVALID;
*id_r = id;
return CKR_OK;
}
/* Return true iff a token is present in slot SLOT. */
bool
slot_token_present (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
return slot->token_present;
}
/* Return the token label. */
char *
slot_token_label (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
/* slots_update() makes sure this is valid. */
return slot->info.serialno;
}
/* Get the manufacturer of the token. */
char *
slot_token_manufacturer (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
unsigned int uval;
/* slots_update() makes sure this is valid. */
uval = xtoi_2 (slot->info.serialno + 16) * 256
+ xtoi_2 (slot->info.serialno + 18);
/* Note: Make sure that there is no colon or linefeed in the string. */
switch (uval)
{
case 0:
/* Fall-through. */
case 0xffff:
return "test card";
case 0x0001:
return "PPC Card Systems";
case 0x0002:
return "Prism";
case 0x0003:
return "OpenFortress";
default:
return "unknown";
}
/* Not reached. */
}
/* Get the manufacturer of the token. */
char *
slot_token_application (slot_iterator_t id)
{
/* slots_update() makes sure this is correct. */
return "OpenPGP";
}
/* Get the serial number of the token. Must not write more than 16
bytes starting from DST. */
int
slot_token_serial (slot_iterator_t id, char *dst)
{
struct slot *slot = scute_table_data (slots, id);
int i;
/* slots_update() makes sure serialno is valid. */
for (i = 0; i < 8; i++)
dst[i] = slot->info.serialno[20 + i];
return 8;
}
/* Get the manufacturer of the token. */
void
slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor,
CK_BYTE *fw_major, CK_BYTE *fw_minor)
{
struct slot *slot = scute_table_data (slots, id);
/* slots_update() makes sure serialno is valid. */
*hw_major = xtoi_2 (slot->info.serialno + 12);
*hw_minor = xtoi_2 (slot->info.serialno + 14);
*fw_major = 0;
*fw_minor = 0;
}
/* Get the maximum and minimum pin length. */
void
slot_token_maxpinlen (slot_iterator_t id, CK_ULONG *max, CK_ULONG *min)
{
struct slot *slot = scute_table_data (slots, id);
*max = MIN (slot->info.chvmaxlen[0], slot->info.chvmaxlen[1]);
/* FIXME: This is true at least for the user pin (CHV1 and CHV2). */
*min = 6;
}
/* Get the maximum and the actual pin count. */
void
slot_token_pincount (slot_iterator_t id, int *max, int *len)
{
struct slot *slot = scute_table_data (slots, id);
*max = 3;
*len = MIN (slot->info.chvretry[0], slot->info.chvretry[1]);
}
/* Return the ID of slot SLOT. */
CK_SLOT_ID
slot_get_id (slot_iterator_t slot)
{
return slot;
}
/* Mechanism management. */
/* Begin iterating over the list of mechanisms. */
CK_RV
mechanisms_iterate_first (slot_iterator_t id,
mechanism_iterator_t *mechanism)
{
struct slot *slot = scute_table_data (slots, id);
*mechanism = scute_table_first (slot->mechanisms);
return CKR_OK;
}
/* Continue iterating over the list of mechanisms. */
CK_RV
mechanisms_iterate_next (slot_iterator_t id, mechanism_iterator_t *mechanism)
{
struct slot *slot = scute_table_data (slots, id);
*mechanism = scute_table_next (slot->mechanisms, *mechanism);
return CKR_OK;
}
/* Return true iff the previous slot was the last one. */
bool
mechanisms_iterate_last (slot_iterator_t id, mechanism_iterator_t *mechanism)
{
struct slot *slot = scute_table_data (slots, id);
return scute_table_last (slot->mechanisms, *mechanism);
}
/* Acquire the mechanism TYPE for the slot id ID. */
CK_RV
mechanisms_lookup (slot_iterator_t id, mechanism_iterator_t *mid_r,
CK_MECHANISM_TYPE type)
{
struct slot *slot = scute_table_data (slots, id);
int mid = scute_table_first (slot->mechanisms);
while (!scute_table_last (slot->mechanisms, mid))
{
struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid);
if (mechanism->type == type)
{
*mid_r = mid;
return CKR_OK;
}
mid = scute_table_next (slot->mechanisms, mid);
}
return CKR_MECHANISM_INVALID;
}
/* Return the type of mechanism MID in slot ID. */
CK_MECHANISM_TYPE
mechanism_get_type (slot_iterator_t id, mechanism_iterator_t mid)
{
struct slot *slot = scute_table_data (slots, id);
struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid);
return mechanism->type;
}
/* Return the info of mechanism MID. */
CK_MECHANISM_INFO_PTR
mechanism_get_info (slot_iterator_t id, mechanism_iterator_t mid)
{
struct slot *slot = scute_table_data (slots, id);
struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid);
return &mechanism->info;
}
/* Session management. */
/* Create a new session. */
CK_RV
slot_create_session (slot_iterator_t id, session_iterator_t *session,
bool rw)
{
int err;
struct slot *slot = scute_table_data (slots, id);
unsigned int tsid;
void *rawp;
struct session *session_p;
assert (slot);
if (scute_table_used (slot->sessions) == SESSION_MAX)
return CKR_SESSION_COUNT;
if (slot->login == SLOT_LOGIN_SO && !rw)
return CKR_SESSION_READ_WRITE_SO_EXISTS;
err = scute_table_alloc (slot->sessions, &tsid, &rawp, NULL);
if (err)
return scute_sys_to_ck (err);
session_p = rawp;
session_p->rw = rw;
session_p->search_result = NULL;
session_p->search_result_len = 0;
session_p->signing_key = CK_INVALID_HANDLE;
*session = SESSION_BUILD_ID (id, tsid);
return CKR_OK;
}
/* Look up session. */
CK_RV
slots_lookup_session (CK_SESSION_HANDLE sid, slot_iterator_t *id,
session_iterator_t *session_id)
{
CK_RV err;
unsigned int idx = SESSION_SLOT (sid);
unsigned session_idx = SESSION_ID (sid);
struct slot *slot;
/* Verify the slot. */
err = slots_lookup (SESSION_SLOT (sid), id);
if (err)
return err;
*session_id = session_idx;
/* Verify the session. */
slot = scute_table_data (slots, idx);
if (!scute_table_data (slot->sessions, session_idx))
return CKR_SESSION_HANDLE_INVALID;
return 0;
}
/* Close the session. */
CK_RV
slot_close_session (slot_iterator_t id, session_iterator_t sid)
{
struct slot *slot = scute_table_data (slots, id);
scute_table_dealloc (slot->sessions, &sid);
/* At last session closed, return to public sessions. */
if (!scute_table_used (slot->sessions))
slot->login = SLOT_LOGIN_PUBLIC;
return CKR_OK;
}
/* Close all sessions. */
CK_RV
slot_close_all_sessions (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
int sid = scute_table_first (slot->sessions);
while (!scute_table_last (slot->sessions, sid))
{
slot_close_session (id, sid);
sid = scute_table_next (slot->sessions, sid);
}
assert (scute_table_used (slot->sessions) == 0);
return CKR_OK;
}
/* Get the RW flag from the session SID in slot ID. */
bool
session_get_rw (slot_iterator_t id, session_iterator_t sid)
{
struct slot *slot = scute_table_data (slots, id);
struct session *session = scute_table_data (slot->sessions, sid);
return session->rw;
}
/* Get the login state from the slot ID. */
slot_login_t
slot_get_status (slot_iterator_t id)
{
struct slot *slot = scute_table_data (slots, id);
return slot->status;
}
/* Object management. */
/* Begin iterating over the list of objects. */
CK_RV
objects_iterate_first (slot_iterator_t id, object_iterator_t *object)
{
struct slot *slot = scute_table_data (slots, id);
*object = scute_table_first (slot->objects);
return CKR_OK;
}
/* Continue iterating over the list of objects. */
CK_RV
objects_iterate_next (slot_iterator_t id, object_iterator_t *object)
{
struct slot *slot = scute_table_data (slots, id);
*object = scute_table_next (slot->objects, *object);
return CKR_OK;
}
/* Return true iff the previous slot was the last one. */
bool
objects_iterate_last (slot_iterator_t id, object_iterator_t *object)
{
struct slot *slot = scute_table_data (slots, id);
return scute_table_last (slot->objects, *object);
}
/* Return the max. number of objects in the slot. May overcount
somewhat. */
CK_RV
slot_get_object_count (slot_iterator_t id, int *nr)
{
struct slot *slot = scute_table_data (slots, id);
*nr = scute_table_used (slot->objects);
return CKR_OK;
}
/* Get the object information for object OBJECT_ID in slot ID. */
CK_RV
slot_get_object (slot_iterator_t id, object_iterator_t oid,
CK_ATTRIBUTE_PTR *obj, CK_ULONG *obj_count)
{
struct slot *slot = scute_table_data (slots, id);
struct object *object = scute_table_data (slot->objects, oid);
if (!object)
return CKR_OBJECT_HANDLE_INVALID;
*obj = object->attributes;
*obj_count = object->attributes_count;
return 0;
}
/* Set the result of a search for session SID in slot ID to
SEARCH_RESULT and SEARCH_RESULT_LEN. */
CK_RV
session_set_search_result (slot_iterator_t id, session_iterator_t sid,
object_iterator_t *search_result,
int search_result_len)
{
struct slot *slot = scute_table_data (slots, id);
struct session *session = scute_table_data (slot->sessions, sid);
if (session->search_result && session->search_result != search_result)
free (session->search_result);
session->search_result = search_result;
session->search_result_len = search_result_len;
return 0;
}
/* Get the stored search result for the session SID in slot ID. */
CK_RV
session_get_search_result (slot_iterator_t id, session_iterator_t sid,
object_iterator_t **search_result,
int *search_result_len)
{
struct slot *slot = scute_table_data (slots, id);
struct session *session = scute_table_data (slot->sessions, sid);
assert (search_result);
assert (search_result_len);
*search_result = session->search_result;
*search_result_len = session->search_result_len;
return 0;
}
/* Set the signing key for session SID in slot ID to KEY. */
CK_RV
session_set_signing_key (slot_iterator_t id, session_iterator_t sid,
object_iterator_t key)
{
struct slot *slot = scute_table_data (slots, id);
struct session *session = scute_table_data (slot->sessions, sid);
CK_RV err;
CK_ATTRIBUTE_PTR attr;
CK_ULONG attr_count;
CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
err = slot_get_object (id, key, &attr, &attr_count);
if (err)
return err;
while (attr_count-- > 0)
if (attr->type == CKA_CLASS)
break;
if (attr_count == (CK_ULONG) -1)
return CKR_KEY_HANDLE_INVALID;
if (attr->ulValueLen != sizeof (key_class)
|| memcmp (attr->pValue, &key_class, sizeof (key_class)))
return CKR_KEY_HANDLE_INVALID;
/* It's the private RSA key object. */
session->signing_key = key;
return 0;
}
/* Set the signing key for session SID in slot ID to KEY. */
CK_RV
session_sign (slot_iterator_t id, session_iterator_t sid,
CK_BYTE_PTR pData, CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
{
struct slot *slot = scute_table_data (slots, id);
struct session *session = scute_table_data (slot->sessions, sid);
gpg_error_t err;
unsigned int sig_len;
/* FIXME: Who cares if they called sign init correctly. Should
check the signing_key object. */
if (pSignature == NULL_PTR)
{
err = scute_agent_sign (NULL, NULL, 0, NULL, &sig_len);
if (err)
return scute_gpg_err_to_ck (err);
*pulSignatureLen = sig_len;
return 0;
}
sig_len = *pulSignatureLen;
err = scute_agent_sign (slot->info.grip3, pData, ulDataLen,
pSignature, &sig_len);
/* FIXME: Oh well. */
if (gpg_err_code (err) == GPG_ERR_INV_ARG)
return CKR_BUFFER_TOO_SMALL;
return scute_gpg_err_to_ck (err);
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 25, 7:14 AM (1 d, 18 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
90/50/c60bfad7eed3f8237d83b8baa49f

Event Timeline