diff --git a/configure.ac b/configure.ac index 3615a49..bc56dae 100644 --- a/configure.ac +++ b/configure.ac @@ -1,486 +1,486 @@ # configure.ac: Configure script for Scute. # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2015 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 this program; if not, see . # # 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. # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s scute-1.n.m) and run "./autogen.sh --force". Please # bump the version number immediately *after* the release and do # another commit and push so that the git magic is able to work. m4_define([mym4_package],[scute]) m4_define([mym4_major], [1]) m4_define([mym4_minor], [6]) m4_define([mym4_micro], [0]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release # with an annotated tag. For example the 1.5 branch starts off with # the tag "scute-1.5-base". This is used as the base for counting # beta numbers before the first release of a series. # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (mym4_isbeta). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor mym4_micro),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # LIBSCUTE_LT_CURRENT=0 LIBSCUTE_LT_AGE=0 LIBSCUTE_LT_REVISION=3 # Version numbers reported by the PKCS #11 module to its users. VERSION_MAJOR=1 VERSION_MINOR=0 -NEED_GPG_ERROR_VERSION=1.14 -NEED_LIBASSUAN_VERSION=2.0.0 -NEED_GPGSM_VERSION=1.9.6 +NEED_GPG_ERROR_VERSION=1.24 +NEED_LIBASSUAN_VERSION=2.5.0 +NEED_GPGSM_VERSION=2.2.0 # Some status variables to give feedback at the end of a configure run. have_gpg_error=no have_libassuan=no # # Provide information about the build. # BUILD_REVISION="mym4_revision" BUILD_REVISION_DEC="mym4_revision_dec" PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/cryptoki.h]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR(m4) AM_INIT_AUTOMAKE AM_MAINTAINER_MODE AC_CANONICAL_HOST # Autobuilder support. AB_INIT # Enable GNU extensions on systems that have them. AC_GNU_SOURCE AH_VERBATIM([_REENTRANT], [/* To allow the use of scute in multithreaded programs we have to use special features from the library. */ #ifndef _REENTRANT # define _REENTRANT 1 #endif]) # Checks for programs. AC_PROG_CC # # Setup gcc specific options # AC_MSG_NOTICE([checking for cc features]) if test "$GCC" = yes; then mycflags= mycflags_save=$CFLAGS # Check whether gcc does not emit a diagnositc for unknow -Wno-* # options. This is the case for gcc >= 4.6 AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) #kickerror #endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) AC_MSG_RESULT($_gcc_silent_wno) # Note that it is okay to use CFLAGS here because these are just # warning options and the user should have a chance of overriding # them. if test "$USE_MAINTAINER_MODE" = "yes"; then mycflags="$mycflags -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" mycflags="$mycflags -Wformat -Wno-format-y2k -Wformat-security" if test x"$_gcc_silent_wno" = xyes ; then _gcc_wopt=yes else AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_wopt=yes],[_gcc_wopt=no]) AC_MSG_RESULT($_gcc_wopt) fi if test x"$_gcc_wopt" = xyes ; then mycflags="$mycflags -W -Wno-sign-compare" mycflags="$mycflags -Wno-missing-field-initializers" fi AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) CFLAGS="-Wdeclaration-after-statement" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) if test x"$_gcc_wopt" = xyes ; then mycflags="$mycflags -Wdeclaration-after-statement" fi else mycflags="$mycflags -Wall" fi if test x"$_gcc_silent_wno" = xyes ; then _gcc_psign=yes else AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign]) CFLAGS="-Wno-pointer-sign" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_psign=yes],[_gcc_psign=no]) AC_MSG_RESULT($_gcc_psign) fi if test x"$_gcc_psign" = xyes ; then mycflags="$mycflags -Wno-pointer-sign" fi AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no) AC_MSG_RESULT($_gcc_psign) if test x"$_gcc_psign" = xyes ; then mycflags="$mycflags -Wpointer-arith" fi CFLAGS="$mycflags $mycflags_save" fi AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable compiler optimization]), [if test $enableval = no ; then CFLAGS=`echo $CFLAGS | sed 's/-O[[0-9]]//'` fi]) AC_SUBST(LIBSCUTE_LT_CURRENT) AC_SUBST(LIBSCUTE_LT_AGE) AC_SUBST(LIBSCUTE_LT_REVISION) AC_DEFINE_UNQUOTED(NEED_GPGSM_VERSION, "$NEED_GPGSM_VERSION", [Min. needed GPGSM version.]) AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) AC_DEFINE_UNQUOTED(VERSION_MAJOR, $VERSION_MAJOR, [Major version number]) AC_DEFINE_UNQUOTED(VERSION_MINOR, $VERSION_MINOR, [Minor version number]) # Don't default to build static libs. # FIXME: Caution: Evil hack ahead. Libtool does not support linking a # static library to a shared library. But for libassuan, we need this. # Instead adding a lot of junk to Makefile.am to get this, we just override # all safety checks here. We are driving without seat belts now! # http://lists.cairographics.org/archives/cairo/2009-April/016962.html lt_cv_deplibs_check_method=pass_all LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") GPGSM_DEFAULT=no GPG_CONNECT_AGENT_DEFAULT=no have_w32_system=no case "${host}" in *-mingw32*) # special stuff for Windoze NT GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe' GPG_CONNECT_AGENT_DEFAULT='c:\\gnupg\\gpg-connect-agent.exe' have_w32_system=yes ;; *) ;; esac if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) # Generate values for the DLL version info if test "$have_w32_system" = yes; then BUILD_TIMESTAMP=`date --iso-8601=minutes` - changequote(,)dnl + changequote(,)dnl BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}${BUILD_REVISION_DEC}" fi AC_SUBST(BUILD_REVISION) AC_SUBST(BUILD_REVISION_DEC) AC_SUBST(BUILD_TIMESTAMP) AC_SUBST(BUILD_FILEVERSION) # Checks for libraries. AC_CHECK_FUNCS([ttyname localtime_r timegm stpcpy]) # Run the checks needed for estream-printf.c estream_PRINTF_INIT # The error code library. Error codes are sent over the IPC layer and # have to be interpreted. AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes, have_gpg_error=no) # The IPC library. AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_VERSION", have_libassuan=yes, have_libassuan=no) # GPGSM NO_OVERRIDE=no AC_ARG_WITH(gpgsm, AC_HELP_STRING([--with-gpgsm=PATH], [use GpgSM binary at PATH]), GPGSM=$withval, NO_OVERRIDE=yes) if test "$NO_OVERRIDE" = "yes" || test "$GPGSM" = "yes"; then GPGSM= NO_OVERRIDE=yes if test "$cross_compiling" != "yes"; then AC_PATH_PROG(GPGSM, gpgsm) fi if test -z "$GPGSM"; then GPGSM="$GPGSM_DEFAULT" fi fi if test "$GPGSM" = no; then if test "$NO_OVERRIDE" = "yes"; then if test "$cross_compiling" != "yes"; then AC_MSG_ERROR([ *** *** Could not find GpgSM, install GpgSM or use --with-gpgsm=PATH to enable it ***]) else AC_MSG_ERROR([ *** *** Can not determine path to GpgSM when cross-compiling, use --with-gpgsm=PATH ***]) fi fi else AC_DEFINE_UNQUOTED(GPGSM_PATH, "$GPGSM", [Path to the GPGSM binary.]) AC_DEFINE(ENABLE_GPGSM,1,[Whether GPGSM support is enabled]) fi AM_CONDITIONAL(HAVE_GPGSM, test "$GPGSM" != "no") dnl Check for GPGSM version requirement. GPGSM_VERSION=unknown ok=maybe if test -z "$GPGSM" -o "x$GPGSM" = "xno"; then ok=no else if test "$cross_compiling" = "yes"; then AC_MSG_WARN([GPGSM version can not be checked when cross compiling]) ok=no else if test ! -x "$GPGSM"; then AC_MSG_WARN([GPGSM not executable, version check disabled]) ok=no fi fi fi if test "$ok" = "maybe"; then AC_MSG_CHECKING(for GPGSM >= $NEED_GPGSM_VERSION) req_major=`echo $NEED_GPGSM_VERSION | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $NEED_GPGSM_VERSION | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $NEED_GPGSM_VERSION | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` gpgsm_version=`$GPGSM --version | grep ^gpgsm` major=`echo $gpgsm_version | \ sed 's/^gpgsm (GnuPG) \([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` minor=`echo $gpgsm_version | \ sed 's/^gpgsm (GnuPG) \([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` micro=`echo $gpgsm_version | \ sed 's/^gpgsm (GnuPG) \([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` GPGSM_VERSION=`echo $gpgsm_version | sed 's/^gpgsm (GnuPG) //'` if test "$major" -gt "$req_major"; then ok=yes else if test "$major" -eq "$req_major"; then if test "$minor" -gt "$req_minor"; then ok=yes else if test "$minor" -eq "$req_minor"; then if test "$micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi if test "$ok" = "yes"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_WARN([GPGSM must be at least version $NEED_GPGSM_VERSION]) fi fi gpgsm_ok="$ok" # GPG_CONNECT_AGENT NO_OVERRIDE=no AC_ARG_WITH(gpg-connect-agent, AC_HELP_STRING([--with-gpg-connect-agent=PATH], [use gpg-connect-agent binary at PATH]), GPG_CONNECT_AGENT=$withval, NO_OVERRIDE=yes) if test "$NO_OVERRIDE" = "yes" || test "$GPG_CONNECT_AGENT" = "yes"; then GPG_CONNECT_AGENT= NO_OVERRIDE=yes if test "$cross_compiling" != "yes"; then AC_CHECK_PROG(GPG_CONNECT_AGENT, gpg-connect-agent, gpg-connect-agent) fi if test -z "$GPG_CONNECT_AGENT"; then GPG_CONNECT_AGENT="$GPG_CONNECT_AGENT_DEFAULT" fi fi if test "$GPG_CONNECT_AGENT" = no; then if test "$NO_OVERRIDE" = "yes"; then if test "$cross_compiling" != "yes"; then AC_MSG_ERROR([ *** *** Could not find gpg-connect-agent, use --with-gpg-connect-agent=PATH to enable it ***]) else AC_MSG_ERROR([ *** *** Can not determine path to gpg-connect-agent when cross-compiling, use --with-gpg-connect-agent=PATH ***]) fi fi else AC_DEFINE_UNQUOTED(GPG_CONNECT_AGENT_PATH, "$GPG_CONNECT_AGENT", [Path to the GPG_CONNECT_AGENT binary.]) fi # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h string.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE # Checks for library functions. # Check for programs needed for the manual. AC_CHECK_PROG(CONVERT, convert, convert) AC_CHECK_PROG(EPSTOPDF, epstopdf, epstopdf) # Test if tests can be run ok=yes AM_CONDITIONAL(RUN_TESTS, test "$ok" = "yes") AH_BOTTOM([ /* Prefix all estream functions. */ #define _ESTREAM_EXT_SYM_PREFIX _scute_ ]) # Print errors here so that they are visible all # together and the user can acquire them all together. die=no if test "$have_gpg_error" = "no"; then die=yes AC_MSG_NOTICE([[ -*** +*** *** You need libgpg-error to build this program. ** This library is for example available at *** ftp://ftp.gnupg.org/pub/gcrypt/libgpg-error *** (at least version $NEED_GPG_ERROR_VERSION is required.) ***]]) fi if test "$have_libassuan" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libassuan to build this program. *** This library is for example available at *** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libassuan/ *** (at least version $NEED_LIBASSUAN_VERSION is required). ***]]) fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi AC_CONFIG_FILES([Makefile m4/Makefile src/Makefile tests/Makefile doc/manual/Makefile doc/Makefile src/versioninfo.rc]) AC_OUTPUT echo " Scute v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host GpgSM: ${GPGSM} Gpg-connect-agent: ${GPG_CONNECT_AGENT} " diff --git a/src/agent.c b/src/agent.c index f8bea95..4fe969b 100644 --- a/src/agent.c +++ b/src/agent.c @@ -1,1205 +1,1192 @@ /* agent.c - Talking to gpg-agent. Copyright (C) 2006, 2007, 2008, 2015 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 #endif #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM #define PATHSEP_C ';' #define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ #include #else #define PATHSEP_C ':' #endif #include #include #include "debug.h" #include "support.h" #include "sexp-parse.h" #include "cert.h" #include "agent.h" /* The global agent context. */ static assuan_context_t agent_ctx = NULL; /* The version number of the agent. */ static int agent_version_major; static int agent_version_minor; /* 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 (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %i\n", (unsigned long)pid, GetLastError ()); #endif } /* Establish a connection to a running GPG agent. */ static gpg_error_t agent_connect (assuan_context_t *ctx_r) { gpg_error_t err = 0; assuan_context_t ctx = NULL; char buffer[255]; FILE *p; /* Use gpg-connect-agent to obtain the socket name * directly from the agent itself. */ snprintf (buffer, sizeof buffer, "%s 'GETINFO socket_name' /bye", get_gpg_connect_agent_path ()); #ifdef HAVE_W32_SYSTEM p = _popen (buffer, "r"); #else p = popen (buffer, "r"); #endif if (p) { int ret; ret = fscanf (p, "D %254s\nOK\n", buffer); if (ret == EOF) /* I/O error? */ err = gpg_error_from_errno (errno); else if (ret != 1) /* Unexpected reply */ err = gpg_error (GPG_ERR_NO_AGENT); pclose (p); } else err = gpg_error_from_errno (errno); /* Then connect to the socket we got. */ if (!err) { err = assuan_new (&ctx); if (!err) { err = assuan_socket_connect (ctx, buffer, 0, 0); if (!err) { *ctx_r = ctx; if (_scute_debug_flags & DBG_ASSUAN) assuan_set_log_stream (*ctx_r, _scute_debug_stream); } else assuan_release (ctx); } } /* We do not try any harder. If gpg-connect-agent somehow failed * to give us a suitable socket, we probably cannot do better. */ if (err) DEBUG (DBG_CRIT, "cannot connect to GPG agent: %s", gpg_strerror (err)); return err; } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t 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 (DBG_CRIT, "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 (DBG_CRIT, "gpg-agent command '%s' failed: %s", optstr, gpg_strerror (err)); free (optstr); return err; } /* Read and stroe the agent's version number. */ static gpg_error_t read_version_cb (void *opaque, const void *buffer, size_t length) { char version[20]; const char *s; (void) opaque; if (length > sizeof (version) -1) length = sizeof (version) - 1; strncpy (version, buffer, length); version[length] = 0; agent_version_major = atoi (version); s = strchr (version, '.'); agent_version_minor = s? atoi (s+1) : 0; return 0; } /* 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 (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; err = agent_simple_cmd (ctx, "OPTION allow-pinentry-notify"); if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; err = assuan_transact (ctx, "GETINFO version", read_version_cb, NULL, NULL, NULL, NULL, NULL); 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 (DBG_CRIT, "GPG Agent connection already established"); return 0; } DEBUG (DBG_INFO, "Establishing connection to gpg-agent"); err = agent_connect (&agent_ctx); if (err) return err; err = agent_configure (agent_ctx); if (err) scute_agent_finalize (); return err; } int scute_agent_get_agent_version (int *minor) { *minor = agent_version_minor; return agent_version_major; } /* 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 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; for (src = hexstr, cnt = 0; *src && !spacep (src); src += 2, cnt++) fpr[cnt] = xtoi_2 (src); return 1; } /* Return true if HEXSTR is a valid keygrip. */ static unsigned int hexgrip_valid_p (const char *hexstr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if ((*s && *s != ' ') || n != 40) return 0; /* Bad keygrip */ else return 1; /* Valid. */ } /* 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->cardtype); free (info->disp_name); free (info->disp_lang); free (info->pubkey_url); free (info->login_data); while (info->kinfo) { key_info_t ki = info->kinfo->next; free (info->kinfo); info->kinfo = ki; } memset (info, 0, sizeof (*info)); } /* Return the key info object for the key KEYREF. If it is not found * NULL is returned. */ key_info_t scute_find_kinfo (agent_card_info_t info, const char *keyref) { key_info_t kinfo; for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next) if (!strcmp (kinfo->keyref, keyref)) return kinfo; return NULL; } /* Create a new key info object with KEYREF. All fields but the * keyref are zeroed out. The created object is appended to the list * at INFO. */ static key_info_t create_kinfo (agent_card_info_t info, const char *keyref) { key_info_t kinfo, ki; kinfo = calloc (1, sizeof *kinfo + strlen (keyref)); if (!kinfo) return NULL; strcpy (kinfo->keyref, keyref); if (!info->kinfo) info->kinfo = kinfo; else { for (ki=info->kinfo; ki->next; ki = ki->next) ; ki->next = kinfo; } return kinfo; } /* FIXME: We are not returning out of memory errors. */ static gpg_error_t learn_status_cb (void *opaque, const char *line) { agent_card_info_t parm = opaque; const char *keyword = line; int keywordlen; key_info_t kinfo; const char *keyref; int i; for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { free (parm->serialno); parm->serialno = store_serialno (line); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { parm->is_piv = !strcmp (line, "PIV"); } else if (keywordlen == 8 && !memcmp (keyword, "CARDTYPE", keywordlen)) { free (parm->cardtype); parm->cardtype = unescape_status_string (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)) { /* The format of such a line is: * KEYPARINFO */ const char *hexgrip = line; while (*line && !spacep (line)) line++; while (spacep (line)) line++; keyref = line; if (hexgrip_valid_p (hexgrip)) { /* Check whether we already have an item for the keyref. */ kinfo = scute_find_kinfo (parm, keyref); if (!kinfo) /* New entry. */ { kinfo = create_kinfo (parm, keyref); if (!kinfo) goto no_core; } else /* Existing entry - clear the grip. */ *kinfo->grip = 0; strncpy (kinfo->grip, hexgrip, sizeof kinfo->grip); kinfo->grip[sizeof kinfo->grip -1] = 0; - - /* Keep legacy info. */ - if (!strcmp (keyref, "OPENPGP.1")) - { - strncpy (parm->grip1, hexgrip, sizeof parm->grip1); - parm->grip1[sizeof parm->grip1 - 1] = 0; - parm->grip1valid = 1; - } - else if (!strcmp (keyref, "OPENPGP.2")) - { - strncpy (parm->grip2, hexgrip, sizeof parm->grip2); - parm->grip2[sizeof parm->grip2 - 1] = 0; - parm->grip2valid = 1; - } - else if (!strcmp (keyref, "OPENPGP.3")) - { - strncpy (parm->grip3, hexgrip, sizeof parm->grip3); - parm->grip3[sizeof parm->grip3 - 1] = 0; - parm->grip3valid = 1; - } } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "gc")) parm->rng_available = abool; /* We're currently not interested in the * other capabilities. */ } } free (buf); } } return 0; no_core: return gpg_error_from_syserror (); } /* 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 --sendinfo", NULL, NULL, default_inq_cb, NULL, learn_status_cb, info); return err; } static gpg_error_t geteventcounter_status_cb (void *opaque, const char *line) { int *result = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 12 && !memcmp (keyword, "EVENTCOUNTER", keywordlen)) { static int any_count; static unsigned int last_count; unsigned int count; if (sscanf (line, "%*u %*u %u ", &count) == 1) { if (any_count && last_count != count) *result = 1; any_count = 1; last_count = count; } } return 0; } 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) { static char last_flag; gpg_error_t err; int any = 0; char flag = '-'; /* First we look at the eventcounter to see if anything happened at all. This is a low overhead function which won't even clutter a gpg-agent log file. There is no need for error checking here. */ if (last_flag) assuan_transact (agent_ctx, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, geteventcounter_status_cb, &any); if (any || !last_flag) { err = assuan_transact (agent_ctx, "SCD GETINFO status", read_status_cb, &flag, default_inq_cb, NULL, NULL, NULL); if (err) return err; last_flag = flag; } else flag = last_flag; if (flag == 'r') return gpg_error (GPG_ERR_CARD_REMOVED); return 0; } /* We only support RSA signatures up to 4096 bits. */ #define MAX_SIGNATURE_BITS 4096 /* Enough space to hold a 4096 bit RSA signature in an S-expression. */ #define MAX_SIGNATURE_LEN 640 /* FIXME: magic value */ 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) { DEBUG (DBG_INFO, "maximum signature length exceeded"); return gpg_error (GPG_ERR_BAD_DATA); } memcpy (&sig->data[sig->len], buffer, length); sig->len += length; return 0; } /* Parse the result of an pksign operation which is a s-expression in - normal form that looks like (7:sig-val(3:rsa(1:s:))). + canonical form that looks like (7:sig-val(3:rsa(1:s:))). The raw result is stored in RESULT of size *LEN, and *LEN is adjusted to the actual size. */ static gpg_error_t pksign_parse_result (const struct signature *sig, unsigned char *result, unsigned int *len) { gpg_error_t err; const unsigned char *s = sig->data; size_t n; int depth; if (*s++ != '(') gpg_error (GPG_ERR_INV_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "sig-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s++ != '(') gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "rsa")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s++ != '(') gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "s")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); - /* Remove nul byte prepended by gpg-agent. */ + /* Remove a possible prepended zero byte. */ if (!*s && n > 1) { n -= 1; s += 1; } if (*len < (unsigned int) n) return gpg_error (GPG_ERR_INV_LENGTH); *len = (unsigned int) n; memcpy (result, s, n); s += n; depth = 3; err = sskip (&s, &depth); if (err) return err; if (s - sig->data != sig->len || depth != 0) return gpg_error (GPG_ERR_INV_SEXP); return 0; } /* Decodes the hash DATA of size LEN (if necessary). Returns a pointer to the raw hash data in R_DATA, the size in R_LEN, and the name of the hash function in R_HASH. Prior to TLSv1.2, the hash function was the concatenation of MD5 and SHA1 applied to the data respectively, and no encoding was applied. From TLSv1.2 on, the hash value is prefixed with an hash identifier and encoded using ASN1. FIXME: Reference. */ static gpg_error_t decode_hash (const unsigned char *data, int len, const unsigned char **r_data, size_t *r_len, const char **r_hash) { static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */ { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */ { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; #define HANDLE(hash,hashlen) \ if (len == sizeof hash ## _prefix + (hashlen) \ && !memcmp (data, hash ## _prefix, sizeof hash ## _prefix)) \ { \ *r_data = data + sizeof hash ## _prefix; \ *r_len = hashlen; \ *r_hash = #hash; \ } if (len == 36) { /* Prior to TLSv1.2, a combination of MD5 and SHA1 was used. */ *r_data = data; *r_len = 36; *r_hash = "tls-md5sha1"; } /* TLSv1.2 encodes the hash value using ASN1. */ else HANDLE (sha1, 20) else HANDLE (rmd160, 20) else HANDLE (sha224, 28) else HANDLE (sha256, 32) else HANDLE (sha384, 48) else HANDLE (sha512, 64) else return gpg_error (GPG_ERR_INV_ARG); #undef HANDLE return 0; } -/* Call the agent to learn about a smartcard. */ + +/* Call the agent to sign (DATA,LEN) using the key described by + * HEXGRIP. Stores the signature in SIG_RESULT and its lengtn at + * SIG_LEN; SIGLEN must initially point to the allocated size of + * SIG_RESULT. */ gpg_error_t -scute_agent_sign (char *grip, unsigned char *data, int len, +scute_agent_sign (const char *hexgrip, unsigned char *data, int len, unsigned char *sig_result, unsigned int *sig_len) { char cmd[150]; gpg_error_t err; const char *hash; const unsigned char *raw_data; size_t raw_len; #define MAX_DATA_LEN 64 /* Size of an SHA512 sum. */ 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); err = decode_hash (data, len, &raw_data, &raw_len, &hash); if (err) return err; if (sig_result == NULL) { *sig_len = raw_len; return 0; } - if (grip == NULL || sig_result == NULL) + if (!hexgrip || !sig_result) return gpg_error (GPG_ERR_INV_ARG); - snprintf (cmd, sizeof (cmd), "SIGKEY %s", grip); + snprintf (cmd, sizeof (cmd), "SIGKEY %s", hexgrip); + err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb, NULL, NULL, NULL); if (err) return err; for (i = 0; i < raw_len; i++) snprintf (&pretty_data[2 * i], 3, "%02X", raw_data[i]); pretty_data[2 * raw_len] = '\0'; snprintf (cmd, sizeof (cmd), "SETHASH --hash=%s %s", hash, 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; err = pksign_parse_result (&sig, sig_result, sig_len); return err; } /* Determine if FPR 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; 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 CERTREF. */ gpg_error_t scute_agent_get_cert (const char *certref, 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 %s", certref); 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 || cert_s.cert_der[0] != 0x30)) { DEBUG (DBG_INFO, "bad card certificate rejected"); err = gpg_error (GPG_ERR_BAD_CERT); } if (err) { if (cert_s.cert_der) free (cert_s.cert_der); return err; } DEBUG (DBG_INFO, "got certificate from card with length %i", cert_s.cert_der_len); cert->cert_der = cert_s.cert_der; cert->cert_der_len = cert_s.cert_der_len; + strncpy (cert->certref, certref, sizeof cert->certref -1); + cert->certref[sizeof cert->certref - 1] = 0; return 0; } struct random_request { unsigned char *buffer; size_t len; }; gpg_error_t get_challenge_data_cb (void *opaque, const void *line, size_t len) { struct random_request *request = opaque; if (len != request->len) return gpg_error (GPG_ERR_INV_LENGTH); memcpy (request->buffer, line, len); return 0; } gpg_error_t scute_agent_get_random (unsigned char *data, size_t len) { char command[16]; gpg_error_t err; struct random_request request; snprintf (command, sizeof(command), "SCD RANDOM %zu", len); request.buffer = data; request.len = len; err = assuan_transact (agent_ctx, command, get_challenge_data_cb, &request, NULL, NULL, NULL, NULL); return err; } void scute_agent_finalize (void) { if (!agent_ctx) { DEBUG (DBG_CRIT, "no GPG Agent connection established"); return; } DEBUG (DBG_INFO, "releasing agent context"); assuan_release (agent_ctx); agent_ctx = NULL; } diff --git a/src/agent.h b/src/agent.h index 3fb2f89..367b7e2 100644 --- a/src/agent.h +++ b/src/agent.h @@ -1,149 +1,143 @@ /* agent.h - Interface for talking to gpg-agent. 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 AGENT_H #define AGENT_H 1 #include #include #include "cert.h" /* An object to store information pertaining to a keypair as stored on * a card. This is commonly used as a linked list of all keys known * for a card. */ struct key_info_s { struct key_info_s *next; char grip[41];/* The keygrip as hex encoded string. */ unsigned char xflag; /* Temporary flag to help processing a list. */ /* The three next items are mostly useful for OpenPGP cards. */ unsigned char fprlen; /* Use length of the next item. */ unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN. */ unsigned long created; /* The time the key was created. */ char keyref[1]; /* String with the keyref (e.g. OPENPGP.1). */ }; typedef struct key_info_s *key_info_t; /* The information structure for a smart card. */ struct agent_card_info_s { char *serialno; /* Malloced hex string. */ char *cardtype; /* Null or mallcoed string with the card type. */ char *disp_name; /* Malloced. */ char *disp_lang; /* Malloced. */ int disp_sex; /* 0 = unspecified, 1 = male, 2 = female. */ char *pubkey_url; /* Malloced. */ char *login_data; /* Malloced. */ char *private_do[4]; /* Malloced. */ char cafpr1valid; char cafpr2valid; char cafpr3valid; char cafpr1[20]; char cafpr2[20]; char cafpr3[20]; key_info_t kinfo; /* Linked list with all keypair related data. */ char fpr1valid; /* Duplicated info for the legacy parts of the code. */ char fpr2valid; char fpr3valid; char fpr1[20]; char fpr2[20]; char fpr3[20]; unsigned int fpr1time; unsigned int fpr2time; unsigned int fpr3time; unsigned long sig_counter; int chv1_cached; /* True if a PIN is not required for each signing. Note that the gpg-agent might cache it anyway. */ int chvmaxlen[3]; /* Maximum allowed length of a CHV. */ int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */ - char grip1valid; - char grip2valid; - char grip3valid; - char grip1[41]; - char grip2[41]; - char grip3[41]; int rng_available; /* True if the GET CHALLENGE operation is supported. */ int is_piv; /* True if this is a PIV card. */ }; typedef struct agent_card_info_s *agent_card_info_t; /* Try to connect to the agent via socket. Handle the server's initial greeting. */ gpg_error_t scute_agent_initialize (void); /* Return the major and minor version of the agent. */ int scute_agent_get_agent_version (int *minor); /* Tear down the agent connection and release all associated resources. */ void scute_agent_finalize (void); /* 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); /* Call the agent to learn about a smartcard. */ gpg_error_t scute_agent_learn (struct agent_card_info_s *info); /* Release the card info structure INFO. */ void scute_agent_release_card_info (struct agent_card_info_s *info); key_info_t scute_find_kinfo (agent_card_info_t info, const char *keyref); -/* Sign the data DATA of length LEN with the key GRIP and return the - signature in SIG_RESULT and SIG_LEN. */ -gpg_error_t scute_agent_sign (char *grip, unsigned char *data, int len, - unsigned char *sig_result, - unsigned int *sig_len); +/* Sign the data DATA of length LEN with the key HEXGRIP and return + * the signature in SIG_RESULT and SIG_LEN. */ +gpg_error_t scute_agent_sign (const char *hexgrip, + unsigned char *data, int len, + unsigned char *sig_result, unsigned int *sig_len); /* Determine if FPR is trusted. */ gpg_error_t scute_agent_is_trusted (char *fpr, bool *is_trusted); /* Try to get certificate for key numer NO. */ gpg_error_t scute_agent_get_cert (const char *certref, struct cert *cert); /* Get random bytes from the card. */ gpg_error_t scute_agent_get_random (unsigned char *data, size_t len); #endif /* AGENT_H */ diff --git a/src/cert-object.c b/src/cert-object.c index 6b559a8..a0f07bd 100644 --- a/src/cert-object.c +++ b/src/cert-object.c @@ -1,817 +1,817 @@ /* 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 #endif #include #include #include #include #include #include #include "cryptoki.h" #include "support.h" #include "cert.h" #include "debug.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)) #if 0 /* Currently not used. */ 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)) { DEBUG (DBG_INFO, "unrepresentable time %i-%i-%i", broken_time.tm_year, broken_time.tm_mon, broken_time.tm_mday); 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; } #endif /*0*/ 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) { DEBUG (DBG_INFO, "unexpected end of certificate"); 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) { DEBUG (DBG_INFO, "unsupported length field"); return gpg_error (GPG_ERR_GENERAL); } if (len < cnt) { DEBUG (DBG_INFO, "unexpected end of certificate"); 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) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != path[i].tag) { DEBUG (DBG_INFO, "wrong element in lookup path"); 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) { DEBUG (DBG_INFO, "unexpected end of certificate"); 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) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != '\x00') { DEBUG (DBG_INFO, "expected binary encapsulation missing"); 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) { DEBUG (DBG_INFO, "modulus too short"); 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) { DEBUG (DBG_INFO, "public exponent too short"); 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) { DEBUG (DBG_CRIT, "out of memory"); 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, +scute_attr_cert (struct cert *cert, const char *grip, CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp) { CK_RV err = 0; 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) { DEBUG (DBG_INFO, "rejecting certificate: could not get subject: %s", gpg_strerror (err)); return err; } err = asn1_get_issuer (cert->cert_der, cert->cert_der_len, &issuer_start, &issuer_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get issuer: %s", gpg_strerror (err)); return err; } err = asn1_get_serial (cert->cert_der, cert->cert_der_len, &serial_start, &serial_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get serial: %s", gpg_strerror (err)); return err; } #define NR_ATTR_CERT 20 attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_CERT); attr_count = 0; if (!attr) { DEBUG (DBG_INFO, "out of memory"); return gpg_error (GPG_ERR_ENOMEM); } if (!err) err = attr_one (attr, &attr_count, CKA_CLASS, &obj_class, sizeof obj_class); if (!err) err = attr_one (attr, &attr_count, CKA_TOKEN, &obj_token, sizeof obj_token); if (!err) err = attr_one (attr, &attr_count, CKA_PRIVATE, &obj_private, sizeof obj_private); if (!err) err = attr_one (attr, &attr_count, CKA_MODIFIABLE, &obj_modifiable, sizeof obj_modifiable); if (!err) - err = attr_one (attr, &attr_count, CKA_LABEL, - &obj_label, sizeof obj_label); + { + if (*cert->certref) + err = attr_one (attr, &attr_count, CKA_LABEL, + cert->certref, strlen (cert->certref)); + else + err = attr_one (attr, &attr_count, CKA_LABEL, + "DummyLabel", 10); + } if (!err) err = attr_one (attr, &attr_count, CKA_CERTIFICATE_TYPE, &obj_cert_type, sizeof obj_cert_type); if (!err) err = attr_one (attr, &attr_count, CKA_TRUSTED, &obj_trusted, sizeof obj_trusted); if (!err) err = attr_one (attr, &attr_count, CKA_CERTIFICATE_CATEGORY, &obj_cert_cat, sizeof obj_cert_cat); /* FIXME: Calculate check_value. */ if (!err) err = attr_one (attr, &attr_count, CKA_CHECK_VALUE, &obj_check_value, sizeof obj_check_value); #if 0 if (time_to_ck_date (&cert->timestamp, &obj_start_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_START_DATE, &obj_start_date, sizeof obj_start_date); } if (time_to_ck_date (&cert->expires, &obj_end_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_END_DATE, &obj_end_date, sizeof obj_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. */ (void)obj_start_date; (void)obj_end_date; if (!err) err = attr_empty (attr, &attr_count, CKA_START_DATE); if (!err) err = attr_empty (attr, &attr_count, CKA_END_DATE); #endif /* Note: This attribute is mandatory. Without it, Firefox client authentication won't work. */ if (!err) err = attr_one (attr, &attr_count, 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. */ + /* We construct the CKA_ID from the CERTREF and the KEYGRIP. This + * allows us to use both values as needed. */ if (!err) - err = attr_one (attr, &attr_count, CKA_ID, - cert->fpr, 40); -#else - { - char certptr[40]; - snprintf (certptr, DIM (certptr), "%p", cert); - if (!err) + { + char cka_id_buffer[200]; + + snprintf (cka_id_buffer, sizeof cka_id_buffer, "%s %s", + *cert->certref ? cert->certref:"-", + grip && *grip? grip : "?" ); err = attr_one (attr, &attr_count, CKA_ID, - certptr, strlen (certptr)); - } -#endif + cka_id_buffer, strlen (cka_id_buffer)); + } + if (!err) err = attr_one (attr, &attr_count, CKA_ISSUER, issuer_start, issuer_len); if (!err) err = attr_one (attr, &attr_count, CKA_SERIAL_NUMBER, serial_start, serial_len); if (!err) err = attr_one (attr, &attr_count, CKA_VALUE, cert->cert_der, cert->cert_der_len); if (!err) err = attr_empty (attr, &attr_count, CKA_URL); if (!err) err = attr_empty (attr, &attr_count, CKA_HASH_OF_SUBJECT_PUBLIC_KEY); if (!err) err = attr_empty (attr, &attr_count, CKA_HASH_OF_ISSUER_PUBLIC_KEY); if (!err) err = attr_one (attr, &attr_count, CKA_JAVA_MIDP_SECURITY_DOMAIN, &obj_java_midp_sec_domain, sizeof obj_java_midp_sec_domain); if (err) { DEBUG (DBG_INFO, "could not build certificate object: %s", gpg_strerror (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) +scute_attr_prv (struct cert *cert, const char *grip, + CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp) { CK_RV err = 0; 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) { DEBUG (DBG_INFO, "rejecting certificate: could not get subject: %s", gpg_strerror (err)); return err; } err = asn1_get_modulus (cert->cert_der, cert->cert_der_len, &modulus_start, &modulus_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get modulus: %s", gpg_strerror (err)); return err; } err = asn1_get_public_exp (cert->cert_der, cert->cert_der_len, &public_exp_start, &public_exp_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get public exp: %s", gpg_strerror (err)); return err; } #define NR_ATTR_PRV 27 attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_PRV); attr_count = 0; if (!attr) { DEBUG (DBG_INFO, "out of core"); return gpg_error (GPG_ERR_ENOMEM); } if (!err) err = attr_one (attr, &attr_count, CKA_CLASS, &obj_class, sizeof obj_class); if (!err) err = attr_one (attr, &attr_count, CKA_TOKEN, &obj_token, sizeof obj_token); if (!err) err = attr_one (attr, &attr_count, CKA_PRIVATE, &obj_private, sizeof obj_private); if (!err) err = attr_one (attr, &attr_count, CKA_MODIFIABLE, &obj_modifiable, sizeof obj_modifiable); if (!err) - err = attr_one (attr, &attr_count, CKA_LABEL, - &obj_label, sizeof obj_label); + { + if (*cert->certref) + err = attr_one (attr, &attr_count, CKA_LABEL, + cert->certref, strlen (cert->certref)); + else + err = attr_one (attr, &attr_count, CKA_LABEL, + "DummyLabel", 10); + } if (!err) err = attr_one (attr, &attr_count, CKA_KEY_TYPE, &obj_key_type, sizeof 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. */ + + /* We construct the CKA_ID from the CERTREF and the KEYGRIP. This + * allows us to use both values as needed. */ if (!err) - err = attr_one (attr, &attr_count, CKA_ID, - &cert->fpr, 40); -#else - { - char certptr[40]; - snprintf (certptr, DIM (certptr), "%p", cert); - if (!err) + { + char cka_id_buffer[200]; + + snprintf (cka_id_buffer, sizeof cka_id_buffer, "%s %s", + *cert->certref ? cert->certref:"-", + grip && *grip? grip : "?" ); err = attr_one (attr, &attr_count, CKA_ID, - certptr, strlen (certptr)); - } -#endif + cka_id_buffer, strlen (cka_id_buffer)); + } #if 0 /* 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. */ /* This code currently only works for certificates retrieved through gpgsm. */ if (time_to_ck_date (&cert->timestamp, &obj_start_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_START_DATE, &obj_start_date, sizeof obj_start_date); } if (time_to_ck_date (&cert->expires, &obj_end_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_END_DATE, &obj_end_date, sizeof obj_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. */ (void)obj_start_date; (void)obj_end_date; if (!err) err = attr_empty (attr, &attr_count, CKA_START_DATE); if (!err) err = attr_empty (attr, &attr_count, CKA_END_DATE); #endif if (!err) err = attr_one (attr, &attr_count, CKA_DERIVE, &obj_derive, sizeof obj_derive); if (!err) err = attr_one (attr, &attr_count, CKA_LOCAL, &obj_local, sizeof obj_local); if (!err) err = attr_one (attr, &attr_count, CKA_KEY_GEN_MECHANISM, &obj_key_gen, sizeof obj_key_gen); if (!err) err = attr_one (attr, &attr_count, CKA_ALLOWED_MECHANISMS, &obj_mechanisms, sizeof obj_mechanisms); if (!err) err = attr_one (attr, &attr_count, CKA_SUBJECT, subject_start, subject_len); if (!err) err = attr_one (attr, &attr_count, CKA_SENSITIVE, &obj_sensitive, sizeof obj_sensitive); if (!err) err = attr_one (attr, &attr_count, CKA_DECRYPT, &obj_decrypt, sizeof obj_decrypt); if (!err) err = attr_one (attr, &attr_count, CKA_SIGN, &obj_sign, sizeof obj_sign); if (!err) err = attr_one (attr, &attr_count, CKA_SIGN_RECOVER, &obj_sign_recover, sizeof obj_sign_recover); if (!err) err = attr_one (attr, &attr_count, CKA_UNWRAP, &obj_unwrap, sizeof obj_unwrap); if (!err) err = attr_one (attr, &attr_count, CKA_EXTRACTABLE, &obj_extractable, sizeof obj_extractable); if (!err) err = attr_one (attr, &attr_count, CKA_ALWAYS_SENSITIVE, &obj_always_sensitive, sizeof obj_always_sensitive); if (!err) err = attr_one (attr, &attr_count, CKA_NEVER_EXTRACTABLE, &obj_never_extractable, sizeof obj_never_extractable); if (!err) err = attr_one (attr, &attr_count, CKA_WRAP_WITH_TRUSTED, &obj_wrap_with_trusted, sizeof obj_wrap_with_trusted); if (!err) err = attr_empty (attr, &attr_count, CKA_UNWRAP_TEMPLATE); if (!err) err = attr_one (attr, &attr_count, CKA_ALWAYS_AUTHENTICATE, &obj_always_authenticate, sizeof obj_always_authenticate); if (!err) err = attr_one (attr, &attr_count, CKA_MODULUS, modulus_start, modulus_len); if (!err) err = attr_one (attr, &attr_count, CKA_PUBLIC_EXPONENT, public_exp_start, public_exp_len); if (err) { DEBUG (DBG_INFO, "could not build private certificate object: %s", gpg_strerror (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 eb09802..b57db0f 100644 --- a/src/cert.h +++ b/src/cert.h @@ -1,136 +1,140 @@ /* 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 #include #include #include #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; + /* The certifciate reference if retrieved from a card or an empty + * string if not known. Example value: "OPENPGP.3". */ + char certref[25]; + #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 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, +gpg_error_t scute_attr_cert (struct cert *cert, const char *grip, 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); +gpg_error_t scute_attr_prv (struct cert *cert, const char *grip, + 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 57a364f..27e5036 100644 --- a/src/gpgsm.c +++ b/src/gpgsm.c @@ -1,160 +1,166 @@ /* gpgsm.c - Talking to gpgsm. 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 #endif #include #include #include #include #include #include #include #include #include "cryptoki.h" #include "support.h" #include "cert.h" #include "agent.h" #include "gpgsm.h" #include "debug.h" -struct search +/* Communication object for search_cb. */ +struct search_cb_parm { bool found; /* Set to true if a private key object was found. */ cert_get_cb_t cert_get_cb; void *hook; bool with_chain; + const char *grip; }; static gpg_error_t search_cb (void *hook, struct cert *cert) { - struct search *ctx = hook; + struct search_cb_parm *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); + err = scute_attr_prv (cert, ctx->grip, &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 (ctx->with_chain && strcmp (cert->chain_id, cert->fpr)) 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); + err = scute_attr_cert (cert, ctx->grip, &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. If * CERTREF is not NULL it is used to locate the cert directly from the * card; if CERTREF is NULL or a cert was not found on the card, GRIP * is used to find the certificate in the local key store of gpgsm. * * FIXME: This is all pretty questionable because our input data * always comes from the card. * * 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, const char *certref, cert_get_cb_t cert_get_cb, void *hook) { gpg_error_t err; - struct search search; + struct search_cb_parm search; search.found = false; search.cert_get_cb = cert_get_cb; search.hook = hook; search.with_chain = false; + search.grip = grip; + + DEBUG (DBG_INFO, "scute_gpgsm_get_cert: certref='%s'", certref); /* If the cert is requested from the card, we try to get it from * the card as well. */ if (certref) { struct cert cert; memset (&cert, '\0', sizeof (cert)); err = scute_agent_get_cert (certref, &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); #endif if (! err) err = search_cb (&search, &cert); return err; } } + DEBUG (DBG_INFO, "scute_gpgsm_get_cert: falling back to gpgsm"); search.with_chain = true; err = scute_gpgsm_search_certs_by_grip (grip, search_cb, &search); return err; } diff --git a/src/p11-gettokeninfo.c b/src/p11-gettokeninfo.c index 3a78598..4094f42 100644 --- a/src/p11-gettokeninfo.c +++ b/src/p11-gettokeninfo.c @@ -1,120 +1,120 @@ /* p11-gettokeninfo.c - Cryptoki implementation. 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 #endif #include "cryptoki.h" #include "locking.h" #include "support.h" #include "settings.h" #include "slots.h" CK_RV CK_SPEC C_GetTokenInfo (CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { CK_RV err = CKR_OK; slot_iterator_t slot; int len; int max; err = scute_global_lock (); if (err) return err; err = slots_lookup (slotID, &slot); if (err) goto out; if (!slot_token_present (slot)) { err = CKR_TOKEN_NOT_PRESENT; goto out; } scute_copy_string (pInfo->label, slot_token_label (slot), 32); scute_copy_string (pInfo->manufacturerID, slot_token_manufacturer (slot), 32); scute_copy_string (pInfo->model, slot_token_application (slot), 16); len = slot_token_serial (slot, pInfo->serialNumber); while (len < 16) pInfo->serialNumber[len++] = ' '; pInfo->flags = CKF_TOKEN_INITIALIZED | CKF_PROTECTED_AUTHENTICATION_PATH | CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED; if (slot_token_has_rng (slot)) pInfo->flags |= CKF_RNG; /* FIXME: CKF_USER_PIN_INITIALIZED only if PIN is not default pin? FIXME: CKF_LOGIN_REQUIRED needed? We could implement login via the "SCD CHECKPIN" command. I am not sure how this mixes with CKF_PROTECTED_AUTHENTICATION_PATH. Not supported: CKF_RESTORE_KEY_NOT_NEEDED, CKF_DUAL_CRYPTO_OPERATIONS. FIXME: We can support those, but do we worry about SO operations? CKF_SO_PIN_COUNT_LOW, CKF_SO_PIN_FINAL_TRY, CKF_SO_PIN_LOCKED. Not supported: CKF_USER_PIN_TO_BE_CHANGED, CKF_SO_PIN_TO_BE_CHANGED. */ slot_token_pincount (slot, &max, &len); if (len < max) pInfo->flags |= CKF_USER_PIN_COUNT_LOW; if (len == 1) pInfo->flags |= CKF_USER_PIN_FINAL_TRY; else if (len == 0) pInfo->flags |= CKF_USER_PIN_LOCKED; - pInfo->ulMaxSessionCount = CK_UNAVAILABLE_INFORMATION; + pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; - pInfo->ulMaxRwSessionCount = CK_UNAVAILABLE_INFORMATION; + pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; slot_token_maxpinlen (slot, &pInfo->ulMaxPinLen, &pInfo->ulMinPinLen); /* FIXME: Get the data from SCD? */ pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; slot_token_version (slot, &pInfo->hardwareVersion.major, &pInfo->hardwareVersion.minor, &pInfo->firmwareVersion.major, &pInfo->firmwareVersion.minor); scute_copy_string (pInfo->utcTime, "0000000000000000", 16); out: scute_global_unlock (); return err; } diff --git a/src/p11-sign.c b/src/p11-sign.c index ceb262e..344bfd8 100644 --- a/src/p11-sign.c +++ b/src/p11-sign.c @@ -1,66 +1,81 @@ /* p11-sign.c - Cryptoki implementation. 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 #endif #include "cryptoki.h" #include "locking.h" #include "slots.h" +/* Sign the data (PDATA,ULDATALEN) using the information recorded in + * the HSESSION by C_SignInit. PSIGNAURE is a buffer to receive the + * signature. The length of that buffer must be stored in a variable + * to which PULSIGNATURELEN points to; on success that length is + * updated to the actual length of the signature in PULSIGNATURE. + * + * If the function returns CKR_BUFFER_TOO_SMALL no further C_SignInit + * is required, instead the function can be called again with a larger + * buffer. On a successful operation CKR_OK is returned and other + * signatures may be created without an new C_SignInit. On all other + * return codes a new C_SignInit is required. + */ CK_RV CK_SPEC C_Sign (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV err = CKR_OK; slot_iterator_t slot; session_iterator_t session; if (pData == NULL_PTR || pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD; err = scute_global_lock (); if (err) return err; err = slots_lookup_session (hSession, &slot, &session); if (err) goto out; - /* FIXME: Who cares if they called sign init correctly. */ + /* FIXME: Check that C_SignInit has been called. */ + err = session_sign (slot, session, pData, ulDataLen, pSignature, pulSignatureLen); out: + /* FIXME: Update the flag which indicates whether C_SignInit has + * been called. */ scute_global_unlock (); return err; } diff --git a/src/p11-signinit.c b/src/p11-signinit.c index 560f37e..598d91d 100644 --- a/src/p11-signinit.c +++ b/src/p11-signinit.c @@ -1,67 +1,71 @@ /* p11-signinit.c - Cryptoki implementation. 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 #endif #include "cryptoki.h" #include "locking.h" #include "slots.h" - +/* Prepare a signature operation. HSESSION is the session's handle. + * PMECHANISM describes the mechanism to be used. HKEY describes the + * key to be used. After calling this function either C_Sign or + * (C_SignUpdate, C_SignFinal) can be used to actually sign the data. + * The preparation is valid until C_Sign or C_SignFinal. */ CK_RV CK_SPEC C_SignInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV err = CKR_OK; slot_iterator_t slot; session_iterator_t session; if (pMechanism == NULL_PTR || pMechanism->mechanism != CKM_RSA_PKCS) return CKR_ARGUMENTS_BAD; if (hKey == CK_INVALID_HANDLE) return CKR_ARGUMENTS_BAD; err = scute_global_lock (); if (err) return err; err = slots_lookup_session (hSession, &slot, &session); if (err) goto out; err = session_set_signing_key (slot, session, hKey); out: scute_global_unlock (); return err; } diff --git a/src/settings.h b/src/settings.h index 40375c7..8f3f4c8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,53 +1,53 @@ /* settings.h - Settings for Scute's Cryptoki implementation. 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 SETTINGS_H #define SETTINGS_H 1 /* Define this to 1 if threads are required for the implementation of the library. */ #define NEED_TO_CREATE_THREADS 0 /* The manufacturer ID in UTF-8. Only up to 32 bytes are used. */ #define MANUFACTURER_ID "g10 Code GmbH" -#define LIBRARY_DESCRIPTION "Cryptoki for SCDaemon" +#define LIBRARY_DESCRIPTION "Cryptoki for GnuPG" /* The version number of this library. Defined by config.h. */ #if (! defined VERSION_MAJOR) || (! defined VERSION_MINOR) #error Define VERSION_MAJOR and VERSION_MINOR in config.h. #endif -/* FIXME: The following should be queried from SCD, really. */ +/* FIXME: The following should be queried from GnuPG, really. */ #define SLOT_DESCRIPTION "GnuPG Smart Card Daemon" #define SLOT_MANUFACTURER_ID "g10 Code GmbH" #define SLOT_HARDWARE_VERSION_MAJOR 0 #define SLOT_HARDWARE_VERSION_MINOR 0 #endif /* !SETTINGS_H */ diff --git a/src/slots.c b/src/slots.c index 5dfc94a..1e9b1a6 100644 --- a/src/slots.c +++ b/src/slots.c @@ -1,1079 +1,1114 @@ /* 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 #endif #include #include #include #include #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" /* 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.ulMaxKeySize = 4096; 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; (void) hook; 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; (void) hook; 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; (void) hook; 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); + key_info_t ki; - err = scute_gpgsm_get_cert (slot->info.grip3, "OPENPGP.3", add_object, slot); - if (err) - goto init_out; + for (ki = slot->info.kinfo; ki; ki = ki->next) + { + err = scute_gpgsm_get_cert (ki->grip, ki->keyref, add_object, slot); + if (err) + goto leave; + } /* FIXME: Perform the rest of the initialization of the token. */ slot->token_present = true; - init_out: + leave: 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 a PIV or an OpenPGP card. FIXME: * Should probably report the error in a better way and use a * generic way to identify cards without resorting to special-casing * PIV cards. */ if (!err && slot->info.is_piv) ; /* Okay, this is a PIV card. */ else if (!err && (!slot->info.serialno || strncmp (slot->info.serialno, "D27600012401", 12) || strlen (slot->info.serialno) != 32)) { DEBUG (DBG_INFO, "token not an OpenPGP card: %s", slot->info.serialno); err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); scute_agent_release_card_info (&slot->info); } /* We also ignore card errors, because unusable cards should not affect slots, and firefox is quite unhappy about returning errors here. */ if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD || gpg_err_code (err) == GPG_ERR_ENODEV) /* 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. */ const char * slot_token_manufacturer (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); unsigned int uval; if (slot->info.is_piv) { if (slot->info.cardtype && !strcmp (slot->info.cardtype, "yubikey")) return "Yubikey"; return "Unknown"; } /* 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 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid AB"; case 0x0005: return "ZeitControl"; case 0x002A: return "Magrathea"; case 0x0000: case 0xffff: return "test card"; default: return (uval & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } /* Not reached. */ } /* Get the application used on the token. */ const char * slot_token_application (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); if (!slot) return "[ooops]"; /* slots_update() makes sure this is correct. */ if (slot->info.is_piv) return "PIV"; else 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; if (slot->info.is_piv) { strncpy (dst, slot->info.serialno, 15); dst[15] = 0; return 16; } /* 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. */ if (slot->info.is_piv) { *hw_major = 0; *hw_minor = 0; *fw_major = 0; *fw_minor = 0; } else { *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); /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *max = slot->info.chvmaxlen[0]; /* 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; /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *len = slot->info.chvretry[0]; } /* Return the ID of slot SLOT. */ CK_SLOT_ID slot_get_id (slot_iterator_t slot) { return slot; } /* Return true if the token supports the GET CHALLENGE operation. */ bool slot_token_has_rng (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->info.rng_available; } /* 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; + /* FIXME: What kind of strange loop is this? */ 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; } -/* FIXME: The dscription is wrong: +/* FIXME: The description is wrong: 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; + CK_ATTRIBUTE_PTR attr; + CK_ULONG attr_count; + CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; unsigned int sig_len; + CK_BYTE key_id[100]; + int i; + const char *keyref; - /* FIXME: Who cares if they called sign init correctly. Should - check the signing_key object. */ + if (!pSignature) + return CKR_ARGUMENTS_BAD; - 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; - } + if (!session->signing_key) + return CKR_OPERATION_NOT_INITIALIZED; + + err = slot_get_object (id, session->signing_key, &attr, &attr_count); + if (err) + return err; + 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; + + /* Find the CKA_ID */ + for (i = 0; i < attr_count; i++) + if (attr[i].type == CKA_ID) + break; + if (i == attr_count) + return CKR_GENERAL_ERROR; + + if (attr[i].ulValueLen >= sizeof key_id - 1) + return CKR_GENERAL_ERROR; + strncpy (key_id, attr[i].pValue, attr[i].ulValueLen); + key_id[attr[i].ulValueLen] = 0; + DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id); + for (keyref=key_id; *keyref && *keyref != ' '; keyref++) + ; + if (*keyref) + keyref++; /* Point to the grip. */ + DEBUG (DBG_INFO, "Using keyref '%s'", keyref); 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; + err = scute_agent_sign (keyref, pData, ulDataLen, pSignature, &sig_len); - return scute_gpg_err_to_ck (err); + /* Take care of error codes which are not mapped by default. */ + if (gpg_err_code (err) == GPG_ERR_INV_LENGTH) + return CKR_BUFFER_TOO_SMALL; + else if (gpg_err_code (err) == GPG_ERR_INV_ARG) + return CKR_ARGUMENTS_BAD; + else + return scute_gpg_err_to_ck (err); } diff --git a/tests/t-auth.c b/tests/t-auth.c index 53c23ed..ba69ccd 100644 --- a/tests/t-auth.c +++ b/tests/t-auth.c @@ -1,160 +1,160 @@ /* t-auth.c - Regression test. 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. */ #include #include #include "t-support.h" CK_RV dump_one (unsigned char *data, int size) { bool some; int i; some = false; for (i = 0; i < size; i++) { if (some == false) { printf (" "); some = true; } printf ("%02x", data[i]); if (((i + 1) % 32) == 0) { printf ("\n"); some = false; } } if (some) printf ("\n"); return 0; } CK_RV sign_with_object (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) { CK_RV err; CK_MECHANISM mechanism = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_BYTE data[36] = "01234567890123456789012345678901234"; CK_BYTE sig[256]; CK_ULONG sig_len = sizeof (sig); err = C_SignInit (session, &mechanism, object); if (err) return err; err = C_Sign (session, data, sizeof (data), sig, &sig_len); if (err) return err; printf (" Sign Result: Length %lu\n", sig_len); err = dump_one (sig, sig_len); if (err) return err; return 0; } int main (int argc, char *argv[]) { CK_RV err; CK_SLOT_ID_PTR slots; CK_ULONG slots_count; unsigned int i; (void) argc; (void) argv; init_cryptoki (); err = C_GetSlotList (true, NULL, &slots_count); fail_if_err (err); if (slots_count == 0) { printf ("Skipping test because no token is present.\n"); return 77; } printf ("Number of slots with tokens: %lu\n", slots_count); slots = malloc (sizeof (CK_SLOT_ID) * slots_count); if (!slots) fail_if_err (CKR_HOST_MEMORY); err = C_GetSlotList (true, slots, &slots_count); fail_if_err (err); for (i = 0; i < slots_count; i++) { CK_SESSION_HANDLE session; CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; CK_ATTRIBUTE attr[] = { { CKA_CLASS, &obj_class, sizeof (obj_class) } }; CK_OBJECT_HANDLE object; CK_ULONG count; printf ("%2i. Slot ID %lu\n", i, slots[i]); err = C_OpenSession (slots[i], CKF_SERIAL_SESSION, NULL, NULL, &session); fail_if_err (err); - + printf (" Session ID: %lu\n", session); err = C_FindObjectsInit (session, attr, DIM (attr)); fail_if_err (err); do { err = C_FindObjects (session, &object, 1, &count); fail_if_err (err); if (count) { printf (" Object Handle: %lu\n", object); err = sign_with_object (session, object); fail_if_err (err); } } while (count); err = C_FindObjectsFinal (session); fail_if_err (err); err = C_CloseSession (session); fail_if_err (err); } return 0; } diff --git a/tests/t-getattribute.c b/tests/t-getattribute.c index a2be7c0..982aaae 100644 --- a/tests/t-getattribute.c +++ b/tests/t-getattribute.c @@ -1,602 +1,627 @@ /* t-getattribute.c - Regression test. 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. */ #include #include #include #include #include "t-support.h" /* If printable characters should be output "as-is". */ bool printable; CK_RV dump_one (CK_ATTRIBUTE_PTR attr, unsigned char *data, unsigned int max_size) { unsigned int i; int col; if (attr->ulValueLen > max_size) return CKR_GENERAL_ERROR; col = 0; for (i = 0; i < attr->ulValueLen; i++) { if (col == 0) printf (" "); if (printable) { if (isprint (data[i])) { printf ("%c", data[i]); col++; } else { printf ("\\x%02x", data[i]); col += 4; } } else { printf ("%02x", data[i]); col += 2; } if (col >= 64) { printf ("\n"); col = 0; } } if (col) printf ("\n"); return 0; } +CK_RV +dump_one_string (CK_ATTRIBUTE_PTR attr, + unsigned char *data, unsigned int max_size) +{ + unsigned int i; + int blanks = 0; + + if (attr->ulValueLen > max_size) + { + putc ('\n', stdout); + return CKR_GENERAL_ERROR; + } + for (i = 0; i < attr->ulValueLen; i++) + { + if (data[i] == ' ') + { + blanks++; + continue; + } + for (; blanks; blanks--) + putc (' ', stdout); + putc (data[i], stdout); + } + putc ('\n', stdout); + + return 0; +} + + CK_RV dump_object (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) { CK_RV err; CK_OBJECT_CLASS obj_class; CK_ATTRIBUTE attr_class = { CKA_CLASS, &obj_class, sizeof (obj_class) }; err = C_GetAttributeValue (session, object, &attr_class, 1); if (err) return err; printf (" Object Class: %lu = ", obj_class); switch (obj_class) { #define MAX_CERT_LEN 4096 case CKO_CERTIFICATE: { CK_CERTIFICATE_TYPE cert_type; CK_BBOOL cert_token; CK_BBOOL cert_private; CK_BBOOL cert_modifiable; CK_BYTE cert_label[MAX_CERT_LEN]; CK_BBOOL cert_trusted; CK_ULONG cert_cc; CK_BYTE cert_check[3]; CK_DATE cert_sdate; CK_DATE cert_edate; CK_BYTE cert_subject[MAX_CERT_LEN]; CK_BYTE cert_id[MAX_CERT_LEN]; CK_BYTE cert_issuer[MAX_CERT_LEN]; CK_BYTE cert_serial[MAX_CERT_LEN]; CK_BYTE cert_value[MAX_CERT_LEN]; CK_ULONG cert_jm; /* Note that the order is encoded below in the various length checks. */ CK_ATTRIBUTE cert_attr[] = { { CKA_CERTIFICATE_TYPE, &cert_type, sizeof (cert_type) }, { CKA_TOKEN, &cert_token, sizeof (cert_token) }, { CKA_PRIVATE, &cert_private, sizeof (cert_private) }, { CKA_MODIFIABLE, &cert_modifiable, sizeof (cert_modifiable) }, { CKA_LABEL, &cert_label, sizeof (cert_label) }, { CKA_TRUSTED, &cert_trusted, sizeof (cert_trusted) }, { CKA_CERTIFICATE_CATEGORY, &cert_cc, sizeof (cert_cc) }, { CKA_CHECK_VALUE, &cert_check, sizeof (cert_check) }, { CKA_START_DATE, &cert_sdate, sizeof (cert_sdate) }, { CKA_END_DATE, &cert_edate, sizeof (cert_edate) }, { CKA_SUBJECT, &cert_subject, sizeof (cert_subject) }, { CKA_ID, &cert_id, sizeof (cert_id) }, { CKA_ISSUER, &cert_issuer, sizeof (cert_issuer) }, { CKA_SERIAL_NUMBER, &cert_serial, sizeof (cert_serial) }, { CKA_VALUE, cert_value, sizeof (cert_value) }, { CKA_URL, NULL, 0 }, { CKA_HASH_OF_SUBJECT_PUBLIC_KEY, NULL, 0 }, { CKA_HASH_OF_ISSUER_PUBLIC_KEY, NULL, 0 }, { CKA_JAVA_MIDP_SECURITY_DOMAIN, &cert_jm, sizeof (cert_jm) } }; printf ("CKO_CERTIFICATE\n"); err = C_GetAttributeValue (session, object, cert_attr, DIM (cert_attr)); if (err) return err; fail_if_err ((cert_attr[0].ulValueLen != sizeof (cert_type)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Type: %lu = ", cert_type); switch (cert_type) { case CKC_X_509: printf ("CKC_X_509"); break; case CKC_WTLS: printf ("CKC_WTLS"); break; case CKC_X_509_ATTR_CERT: printf ("CKC_X_509_ATTR_CERT"); break; default: printf ("(unknown"); break; } printf ("\n"); fail_if_err ((cert_attr[1].ulValueLen != sizeof (cert_token)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Token: %s\n", cert_token ? "true" : "false"); fail_if_err ((cert_attr[2].ulValueLen != sizeof (cert_private)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Private: %s\n", cert_private ? "true" : "false"); fail_if_err ((cert_attr[3].ulValueLen != sizeof (cert_modifiable)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Modifiable: %s\n", cert_modifiable ? "true" : "false"); - printf (" Certificate Label: Length %lu\n", - cert_attr[4].ulValueLen); - err = dump_one (&cert_attr[4], cert_label, sizeof (cert_label)); + printf (" Certificate Label: "); + err = dump_one_string (&cert_attr[4], cert_label, sizeof (cert_label)); fail_if_err (err); fail_if_err ((cert_attr[5].ulValueLen != sizeof (cert_trusted)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Trusted: %s\n", cert_trusted ? "true" : "false"); fail_if_err ((cert_attr[6].ulValueLen != sizeof (cert_cc)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Category: %lu = ", cert_cc); switch (cert_cc) { case 0: printf ("unspecified"); break; case 1: printf ("token user"); break; case 2: printf ("authority"); break; case 3: printf ("other entity"); break; default: printf ("(unknown)"); break; } printf ("\n"); fail_if_err ((cert_attr[7].ulValueLen != sizeof (cert_check)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Check Value: %02x%02x%02x\n", cert_check[0], cert_check[1], cert_check[2]); if (cert_attr[8].ulValueLen && cert_attr[9].ulValueLen) { fail_if_err ((cert_attr[8].ulValueLen != sizeof (cert_sdate)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Start Date: %.4s/%.2s/%.2s\n", cert_sdate.year, cert_sdate.month, cert_sdate.day); fail_if_err ((cert_attr[9].ulValueLen != sizeof (cert_edate)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate End Date: %.4s/%.2s/%.2s\n", cert_edate.year, cert_edate.month, cert_edate.day); } printf (" Certificate Subject: Length %lu\n", cert_attr[10].ulValueLen); err = dump_one (&cert_attr[10], cert_subject, sizeof (cert_subject)); fail_if_err (err); - printf (" Certificate ID: Length %lu\n", - cert_attr[11].ulValueLen); - err = dump_one (&cert_attr[11], cert_id, sizeof (cert_id)); + printf (" Certificate ID: "); + err = dump_one_string (&cert_attr[11], cert_id, sizeof (cert_id)); fail_if_err (err); printf (" Certificate Issuer: Length %lu\n", cert_attr[12].ulValueLen); err = dump_one (&cert_attr[12], cert_issuer, sizeof (cert_issuer)); fail_if_err (err); printf (" Certificate Serial Number: Length %lu\n", cert_attr[13].ulValueLen); err = dump_one (&cert_attr[13], cert_serial, sizeof (cert_serial)); fail_if_err (err); printf (" Certificate Value: Length %lu\n", cert_attr[14].ulValueLen); err = dump_one (&cert_attr[14], cert_value, sizeof (cert_value)); fail_if_err (err); fail_if_err ((cert_attr[15].ulValueLen != 0) ? CKR_GENERAL_ERROR : 0); fail_if_err ((cert_attr[16].ulValueLen != 0) ? CKR_GENERAL_ERROR : 0); fail_if_err ((cert_attr[17].ulValueLen != 0) ? CKR_GENERAL_ERROR : 0); fail_if_err ((cert_attr[18].ulValueLen != sizeof (cert_jm)) ? CKR_GENERAL_ERROR : 0); printf (" Certificate Java MIDP Security Domain: %lu = ", cert_jm); switch (cert_jm) { case 0: printf ("unspecified"); break; case 1: printf ("manufacturer"); break; case 2: printf ("operator"); break; case 3: printf ("third party"); break; default: printf ("(unknown)"); break; } printf ("\n"); } break; case CKO_PRIVATE_KEY: { CK_KEY_TYPE key_type; CK_BBOOL key_token; CK_BBOOL key_private; CK_BBOOL key_modifiable; CK_BYTE key_label[MAX_CERT_LEN]; CK_BYTE key_id[MAX_CERT_LEN]; CK_DATE key_sdate; CK_DATE key_edate; CK_BBOOL key_derive; CK_BBOOL key_local; CK_MECHANISM_TYPE key_gen; CK_MECHANISM_TYPE key_mechanisms[1]; /* FIXME, hard-coded constant. */ CK_BYTE key_subject[MAX_CERT_LEN]; CK_BBOOL key_sensitive; CK_BBOOL key_decrypt; CK_BBOOL key_sign; CK_BBOOL key_sign_recover; CK_BBOOL key_unwrap; CK_BBOOL key_extractable; CK_BBOOL key_always_sensitive; CK_BBOOL key_never_extractable; CK_BBOOL key_wrap_with_trusted; CK_BBOOL key_always_authenticate; CK_BYTE key_modulus[MAX_CERT_LEN]; CK_BYTE key_public_exp[MAX_CERT_LEN]; /* Note that the order is encoded below in the various length checks. */ CK_ATTRIBUTE key_attr[] = { { CKA_KEY_TYPE, &key_type, sizeof (key_type) }, { CKA_TOKEN, &key_token, sizeof (key_token) }, { CKA_PRIVATE, &key_private, sizeof (key_private) }, { CKA_MODIFIABLE, &key_modifiable, sizeof (key_modifiable) }, { CKA_LABEL, &key_label, sizeof (key_label) }, { CKA_ID, &key_id, sizeof (key_id) }, { CKA_START_DATE, &key_sdate, sizeof (key_sdate) }, { CKA_END_DATE, &key_edate, sizeof (key_edate) }, { CKA_DERIVE, &key_derive, sizeof (key_derive) }, { CKA_LOCAL, &key_local, sizeof (key_local) }, { CKA_KEY_GEN_MECHANISM, &key_gen, sizeof (key_gen) }, { CKA_ALLOWED_MECHANISMS, &key_mechanisms, sizeof (key_mechanisms) }, { CKA_SUBJECT, &key_subject, sizeof (key_subject) }, { CKA_SENSITIVE, &key_sensitive, sizeof (key_sensitive) }, { CKA_DECRYPT, &key_decrypt, sizeof (key_decrypt) }, { CKA_SIGN, &key_sign, sizeof (key_sign) }, { CKA_SIGN_RECOVER, &key_sign_recover, sizeof (key_sign_recover) }, { CKA_UNWRAP, &key_unwrap, sizeof (key_unwrap) }, { CKA_EXTRACTABLE, &key_extractable, sizeof (key_extractable) }, { CKA_ALWAYS_SENSITIVE, &key_always_sensitive, sizeof (key_always_sensitive) }, { CKA_NEVER_EXTRACTABLE, &key_never_extractable, sizeof (key_never_extractable) }, { CKA_WRAP_WITH_TRUSTED, &key_wrap_with_trusted, sizeof (key_wrap_with_trusted) }, { CKA_UNWRAP_TEMPLATE, NULL, 0 }, { CKA_ALWAYS_AUTHENTICATE, &key_always_authenticate, sizeof (key_always_authenticate) }, { CKA_MODULUS, &key_modulus, sizeof (key_modulus) }, { CKA_PUBLIC_EXPONENT, &key_public_exp, sizeof (key_public_exp) } }; printf ("CKO_PRIVATE_KEY\n"); err = C_GetAttributeValue (session, object, key_attr, DIM (key_attr)); if (err) return err; fail_if_err ((key_attr[0].ulValueLen != sizeof (key_type)) ? CKR_GENERAL_ERROR : 0); printf (" Key Type: %lu = ", key_type); switch (key_type) { case CKK_RSA: printf ("CKK_RSA"); break; case CKK_DSA: printf ("CKK_DSA"); break; default: printf ("(unknown"); break; } printf ("\n"); fail_if_err ((key_attr[1].ulValueLen != sizeof (key_token)) ? CKR_GENERAL_ERROR : 0); printf (" Key Token: %s\n", key_token ? "true" : "false"); fail_if_err ((key_attr[2].ulValueLen != sizeof (key_private)) ? CKR_GENERAL_ERROR : 0); printf (" Key Private: %s\n", key_private ? "true" : "false"); fail_if_err ((key_attr[3].ulValueLen != sizeof (key_modifiable)) ? CKR_GENERAL_ERROR : 0); printf (" Key Modifiable: %s\n", key_modifiable ? "true" : "false"); - printf (" Key Label: Length %lu\n", - key_attr[4].ulValueLen); - err = dump_one (&key_attr[4], key_label, sizeof (key_label)); + printf (" Key Label: "); + err = dump_one_string (&key_attr[4], key_label, sizeof (key_label)); fail_if_err (err); - printf (" Key ID: Length %lu\n", - key_attr[5].ulValueLen); - err = dump_one (&key_attr[5], key_id, sizeof (key_id)); + printf (" Key ID: "); + err = dump_one_string (&key_attr[5], key_id, sizeof (key_id)); fail_if_err (err); if (key_attr[6].ulValueLen && key_attr[7].ulValueLen) { fail_if_err ((key_attr[6].ulValueLen != sizeof (key_sdate)) ? CKR_GENERAL_ERROR : 0); printf (" Key Start Date: %.4s/%.2s/%.2s\n", key_sdate.year, key_sdate.month, key_sdate.day); fail_if_err ((key_attr[7].ulValueLen != sizeof (key_edate)) ? CKR_GENERAL_ERROR : 0); printf (" Key End Date: %.4s/%.2s/%.2s\n", key_edate.year, key_edate.month, key_edate.day); } fail_if_err ((key_attr[8].ulValueLen != sizeof (key_derive)) ? CKR_GENERAL_ERROR : 0); printf (" Key Derive: %s\n", key_derive ? "true" : "false"); fail_if_err ((key_attr[9].ulValueLen != sizeof (key_local)) ? CKR_GENERAL_ERROR : 0); printf (" Key Local: %s\n", key_local ? "true" : "false"); fail_if_err ((key_attr[10].ulValueLen != sizeof (key_gen)) ? CKR_GENERAL_ERROR : 0); /* FIXME: Print Mechanism. */ printf (" Key Gen Mechanism: %lu\n", key_gen); /* FIXME: Print supported mechanisms. 11 */ printf (" Key Subject: Length %lu\n", key_attr[12].ulValueLen); err = dump_one (&key_attr[12], key_subject, sizeof (key_subject)); fail_if_err (err); fail_if_err ((key_attr[13].ulValueLen != sizeof (key_sensitive)) ? CKR_GENERAL_ERROR : 0); printf (" Key Sensitive: %s\n", key_sensitive ? "true" : "false"); fail_if_err ((key_attr[14].ulValueLen != sizeof (key_decrypt)) ? CKR_GENERAL_ERROR : 0); printf (" Key Decrypt: %s\n", key_decrypt ? "true" : "false"); fail_if_err ((key_attr[15].ulValueLen != sizeof (key_sign)) ? CKR_GENERAL_ERROR : 0); printf (" Key Sign: %s\n", key_sign ? "true" : "false"); fail_if_err ((key_attr[16].ulValueLen != sizeof (key_sign_recover)) ? CKR_GENERAL_ERROR : 0); printf (" Key Sign Recover: %s\n", key_sign_recover ? "true" : "false"); fail_if_err ((key_attr[17].ulValueLen != sizeof (key_unwrap)) ? CKR_GENERAL_ERROR : 0); printf (" Key Unwrap: %s\n", key_unwrap ? "true" : "false"); fail_if_err ((key_attr[18].ulValueLen != sizeof (key_extractable)) ? CKR_GENERAL_ERROR : 0); printf (" Key Extractable: %s\n", key_extractable ? "true" : "false"); fail_if_err ((key_attr[19].ulValueLen != sizeof (key_always_sensitive)) ? CKR_GENERAL_ERROR : 0); printf (" Key Always Sensitive: %s\n", key_always_sensitive ? "true" : "false"); fail_if_err ((key_attr[20].ulValueLen != sizeof (key_never_extractable)) ? CKR_GENERAL_ERROR : 0); printf (" Key Never Extractable: %s\n", key_never_extractable ? "true" : "false"); fail_if_err ((key_attr[21].ulValueLen != sizeof (key_wrap_with_trusted)) ? CKR_GENERAL_ERROR : 0); printf (" Key Wrap With Trusted: %s\n", key_wrap_with_trusted ? "true" : "false"); fail_if_err ((key_attr[22].ulValueLen != 0) ? CKR_GENERAL_ERROR : 0); fail_if_err ((key_attr[23].ulValueLen != sizeof (key_always_authenticate)) ? CKR_GENERAL_ERROR : 0); printf (" Key Always Authenticate: %s\n", key_always_authenticate ? "true" : "false"); printf (" Key Modulus: Length %lu\n", key_attr[24].ulValueLen); err = dump_one (&key_attr[24], key_modulus, sizeof (key_modulus)); fail_if_err (err); printf (" Key Subject: Length %lu\n", key_attr[25].ulValueLen); err = dump_one (&key_attr[25], key_public_exp, sizeof (key_public_exp)); fail_if_err (err); } break; default: printf ("(unknown)\n"); } return 0; } int main (int argc, char *argv[]) { CK_RV err; CK_SLOT_ID_PTR slots; CK_ULONG slots_count; unsigned int i; (void) argc; (void) argv; if (argc > 1 && !strcmp ("--printable", argv[1])) printable = true; init_cryptoki (); err = C_GetSlotList (true, NULL, &slots_count); fail_if_err (err); if (slots_count == 0) { printf ("Skipping test because no token is present.\n"); return 77; } printf ("Number of slots with tokens: %lu\n", slots_count); slots = malloc (sizeof (CK_SLOT_ID) * slots_count); if (!slots) fail_if_err (CKR_HOST_MEMORY); err = C_GetSlotList (true, slots, &slots_count); fail_if_err (err); for (i = 0; i < slots_count; i++) { CK_SESSION_HANDLE session; CK_OBJECT_HANDLE object; CK_ULONG count; printf ("%2i. Slot ID %lu\n", i, slots[i]); err = C_OpenSession (slots[i], CKF_SERIAL_SESSION, NULL, NULL, &session); fail_if_err (err); printf (" Session ID: %lu\n", session); err = C_FindObjectsInit (session, NULL, 0); fail_if_err (err); do { err = C_FindObjects (session, &object, 1, &count); fail_if_err (err); if (count) { printf (" Object Handle: %lu\n", object); err = dump_object (session, object); fail_if_err (err); } } while (count); err = C_FindObjectsFinal (session); fail_if_err (err); err = C_CloseSession (session); fail_if_err (err); } return 0; } diff --git a/tests/t-getinfo.c b/tests/t-getinfo.c index b90f132..f246254 100644 --- a/tests/t-getinfo.c +++ b/tests/t-getinfo.c @@ -1,66 +1,66 @@ /* t-getinfo.c - Regression test. 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. */ #include #include #include "t-support.h" int main (int argc, char *argv[]) { CK_RV err; CK_INFO info; (void) argc; (void) argv; init_cryptoki (); err = C_GetInfo (&info); fail_if_err (err); - printf ("Cryptoki version: %i.%i\n", info.cryptokiVersion.major, + printf ("Cryptoki version: %i.%i\n", info.cryptokiVersion.major, info.cryptokiVersion.minor); if (info.cryptokiVersion.major != 2) fail ("Cryptoki major version is not 2"); if (info.cryptokiVersion.minor != 20) fail ("Cryptoki minor version is not 20"); printf ("Manufacturer ID: %.32s\n", info.manufacturerID); printf ("Flags: %#lx\n", info.flags); if (info.flags != 0) fail ("Flags is not 0"); printf ("Library description: %.32s\n", info.libraryDescription); - printf ("Library version: %i.%i\n", info.cryptokiVersion.major, - info.cryptokiVersion.minor); + printf ("Library version: %i.%i\n", info.libraryVersion.major, + info.libraryVersion.minor); return 0; }