pax_global_header00006660000000000000000000000064131622766210014520gustar00rootroot0000000000000052 comment=ab08cd32596975588cdf61d4dcb7c95bc301d447 gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/000077500000000000000000000000001316227662100175035ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/.gitignore000066400000000000000000000007501316227662100214750ustar00rootroot00000000000000*.bz2 *.exe *.gz *.html *.o *~ .deps Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh missing stamp-h1 gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy-server gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.service gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.service.in gnupg-pkcs11-scd.spec gnupg-pkcs11-scd/gnupg-pkcs11-scd copyright gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/AUTHORS000066400000000000000000000001061316227662100205500ustar00rootroot00000000000000Zeljko Vrba Alon Bar-Lev gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/COPYING000066400000000000000000000052721316227662100205440ustar00rootroot00000000000000 gnupg-pkcs11-scd License Copyright (c) 2006-2007 Zeljko Vrba Copyright (c) 2006-2017 Alon Bar-Lev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pkcs11-helper License Copyright (c) 2005-2007 Alon Bar-Lev All rights reserved. m4 Macros License autoconf/automake Copyright (C) 2002, 2003 Free Software Foundation, Inc. pkg.m4 Copyright © 2004 Scott James Remnant . OpenSSL License (Optional component, handling crypto) This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.OpenSSL.org/) This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com). GNUTLS License (Optional component, for an optional complete GPLed license, replaces OpenSSL) Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Nikos Mavroyanopoulos See the end for copying conditions. The copyright holder for Gnutls is Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. libgcrypt License Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 2004, 2006 Free Software Foundation, Inc. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/ChangeLog000066400000000000000000000102541316227662100212570ustar00rootroot00000000000000gnupg-pkcs11-scd Copyright (c) 2006-2007 Zeljko Vrba Copyright (c) 2006-2017 Alon Bar-Lev 2017-09-26 - Version 0.9.1 * Support unix domain socket credentials on FreeBSD. * Introduce GNUPG_PKCS11_SOCKETDIR to instruct where sockets are created. * Make proxy systemd service work again per change of systemd behavior. 2017-08-25 - Version 0.9.0 * Avoid dup of stdin/stdout so that the terminate assuan hack operational again. * Introduce gnupg-pkcs11-scd-proxy to allow isolation of the PKCS#11 provider. * Lots of cleanups. 2017-07-15 - Version 0.8.0 * Support multiple tokens via serial numbers by hashing token id into serial number. Implementation changes the card serial number yet again, executing gpg --card-status should resync. 2017-04-18 - Version 0.7.6 * Add --homedir parameter. * Rework serial responses for gnupg-2.1.19. 2017-03-01 - Version 0.7.5 * Fix issue with decrypting padded data, thanks to smunaut. * Catchup with gnupg-2.1 changes which caused inability to support both gpg and gpgsm. Implementation had to change card serial number, as a result current keys of gpg will look for the previous serial card. emulate-openpgpg option is obsoleted and removed. ACTION REQUIRED in order to assign new card serial number to existing keys. backup your ~/.gnupg. delete all PKCS#11 secret keys using: gpg --delete-secret-keys $KEY then Then refresh keys using: gpg --card-edit In Copyright (c) 2006-2017 Alon Bar-Lev POSIX Dependencies: dl pthread (nptl) pkcs11-helper http://www.opensc-project.org || ( OpenSSL>=0.9.7 http://www.openssl.org GNUTLS>=1.4.4 http://www.gnutls.org ) libgpg-error>=1.3 http://www.gnupg.org libassuan>=0.9.2 http://www.gnupg.org libgcrypt>=1.2.2 http://www.gnupg.org Build: $ ./configure $ make $ make install Cross-MinGW32 Notes: Supported single threaded server mode. Implementing multi-threaded server forces copy of win32 socket handling from gnupg, and it is GPLed. When libassuan will handle sockets we will be able to implement it. Dependencies: man2html pkcs11-helper http://www.opensc-project.org OpenSSL>=0.9.9 http://www.openssl.org libgpg-error>=1.3 http://www.gnupg.org libassuan>=0.9.2 http://www.gnupg.org libgcrypt>=1.2.2 http://www.gnupg.org Build: w32root=/tmp/gpg-win32 OpenSSL ./Configure --prefix=// --cross-compiler-prefix=mingw32- shared mingw make install INSTALL_PREFIX="${w32root}" libgpg-error ./autogen.sh --build-w32 make install libassuan ./autogen.sh --build-w32 make install libgcrypt ./autogen.sh --build-w32 make install gnupg-pkcs11-scd ./configure --prefix=/ --host=mingw32 \ --with-libgpg-error-prefix=${w32root} \ --with-libassuan-prefix=${w32root} \ --with-libgcrypt-prefix=${w32root} \ PKG_CONFIG=true \ OPENSSL_CFLAGS="-I${w32root}/include" \ OPENSSL_LIBS="-L${w32root}/lib -lcrypto" \ PKCS11_HELPER_CFLAGS="-I${w32root}/include" \ PKCS11_HELPER_LIBS="-L${w32root}/lib -lpkcs11-helper" \ PKCS11_HELPER_FEATURES="threading token certificate engine_crypto" make install DESTDIR="${w32root}" Native-Cygwin Notes: Same as Cross-MinGW32 Dependencies: Same as Cross-MinGW32 Build: Same as Cross-MinGW32, replace --host=mingw32 with --with-cygwin-native. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/Makefile.am000066400000000000000000000044001316227662100215350ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # AUTOMAKE_OPTIONS = foreign dist-bzip2 ACLOCAL_AMFLAGS = -I m4 MAINTAINERCLEANFILES = \ config.log config.status \ $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \ $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ $(srcdir)/depcomp $(srcdir)/compile $(srcdir)/aclocal.m4 \ $(srcdir)/config.guess $(srcdir)/config.sub \ $(srcdir)/m4/ltsugar.m4 $(srcdir)/m4/libtool.m4 \ $(srcdir)/m4/ltversion.m4 $(srcdir)/m4/lt~obsolete.m4 \ $(srcdir)/m4/ltoptions.m4 \ $(NULL) SUBDIRS = \ gnupg-pkcs11-scd \ gnupg-pkcs11-scd-proxy \ distro \ $(NULL) doc_DATA = \ COPYING \ README \ $(NULL) gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/README000066400000000000000000000010551316227662100203640ustar00rootroot00000000000000gnupg-pkcs11-scd -- PKCS#11 enabled gnupg scd. Copyright (c) 2006-2007 Zeljko Vrba Copyright (c) 2006-2017 Alon Bar-Lev ABOUT gnupg-pkcs11 is a project to implement a BSD-licensed smart-card daemon to enable the use of PKCS#11 tokens with GnuPG. PKCS#11 is the de-facto standard for accessing cryptographic tokens, and thus we strongly disagree with WK's attitude towards it. AUTHORS Zeljko Vrba Alon Bar-Lev SUPPORT http://gnupg-pkcs11.sourceforge.net/ gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/THANKS000066400000000000000000000003131316227662100204130ustar00rootroot00000000000000 Eddy Nigg - For maintaining RPM support. Sandro Wefel - For maintaining Debian packaging. Apologies to anyone we have missed. Zeljko Vrba Alon Bar-Lev gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/configure.ac000066400000000000000000000251401316227662100217730ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # AC_PREREQ([2.60]) define([PACKAGE_VERSION_MAJOR], [0]) define([PACKAGE_VERSION_MINOR], [9]) define([PACKAGE_VERSION_FIX], [1]) define([PACKAGE_SUFFIX], []) AC_INIT([gnupg-pkcs11-scd], [PACKAGE_VERSION_MAJOR.PACKAGE_VERSION_MINOR.PACKAGE_VERSION_FIX[]PACKAGE_SUFFIX]) AC_CONFIG_AUX_DIR([.]) AM_CONFIG_HEADER([config.h]) AC_CONFIG_SRCDIR([gnupg-pkcs11-scd/common.h]) AM_INIT_AUTOMAKE GNUPG_PKCS11_SCD_VERSION_MAJOR="PACKAGE_VERSION_MAJOR" GNUPG_PKCS11_SCD_VERSION_MINOR="PACKAGE_VERSION_MINOR" GNUPG_PKCS11_SCD_VERSION_FIX="PACKAGE_VERSION_FIX" AC_SUBST([GNUPG_PKCS11_SCD_VERSION_MAJOR]) AC_SUBST([GNUPG_PKCS11_SCD_VERSION_MINOR]) AC_SUBST([GNUPG_PKCS11_SCD_VERSION_FIX]) AC_USE_SYSTEM_EXTENSIONS AC_CANONICAL_HOST AC_PROG_CC AC_PROG_INSTALL PKG_PROG_PKG_CONFIG AC_CHECK_PROGS([M4], [m4]) AC_ARG_WITH( [cygwin-native], [AC_HELP_STRING([--with-cygwin-native], [compile native win32])], , [with_cygwin_native="no"] ) test -z "${HAVE_W32_SYSTEM}" && HAVE_W32_SYSTEM="no" test -z "${CYGWIN}" && CYGWIN="no" case "${host}" in *-mingw*) AC_DEFINE([HAVE_W32_SYSTEM], [1], [Defined if we run on a W32 API based system]) HAVE_W32_SYSTEM="yes" ;; *-winnt*) AC_DEFINE([HAVE_W32_SYSTEM], [1], [Defined if we run on a W32 API based system]) HAVE_W32_SYSTEM="yes" ;; *-cygwin*) AC_MSG_CHECKING([cygwin mode to use]) CYGWIN="yes" if test "${CYGWIN_NATIVE}" = "yes"; then AC_MSG_RESULT([Using native win32]) CFLAGS="${CFLAGS} -mno-cygwin" HAVE_W32_SYSTEM="yes" else AC_MSG_RESULT([Using cygwin]) fi ;; *) ;; esac AC_ARG_ENABLE( [strict], [AC_HELP_STRING([--enable-strict], [enable strict compiler warnings])], , [enable_strict="no"] ) AC_ARG_ENABLE( [pedantic], [AC_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])], , [enable_pedantic="no"] ) AC_ARG_ENABLE( [proxy], [AC_HELP_STRING([--enable-proxy], [enable gnupg-pkcs11-scd-proxy])], , [enable_proxy="no"] ) AC_ARG_WITH( [proxy-socket], [AC_HELP_STRING([--with-proxy-socket=FILE], [set proxy socket @<:@LOCALSTATEDIR/run/gnupg-pkcs11-scd-proxy/cmd@:>@])], , [with_proxy_socket="\${localstatedir}/run/gnupg-pkcs11-scd-server/cmd"] ) AC_ARG_WITH( [proxy-user], [AC_HELP_STRING([--with-proxy-user=USER], [set proxy user @<:@gnupg-pkcs11-scd-proxy@:>@])], , [with_proxy_user="gnupg-pkcs11-scd-proxy"] ) AC_ARG_WITH( [proxy-user-group], [AC_HELP_STRING([--with-proxy-user-group=GROUP], [set proxy user group @<:@gnupg-pkcs11-scd-proxy@:>@])], , [with_proxy_user_group="gnupg-pkcs11-scd-proxy"] ) AC_ARG_WITH( [proxy-group], [AC_HELP_STRING([--with-proxy-group=GROUP], [set proxy group @<:@gnupg-pkcs11@:>@])], , [with_proxy_group="gnupg-pkcs11"] ) AC_ARG_WITH( [openssl], [AC_HELP_STRING([--without-openssl], [disable OpenSSL linkage)])], , [with_openssl="yes"] ) AC_ARG_WITH( [gnutls], [AC_HELP_STRING([--without-gnutls], [disable GNUTLS linkage (OpenSSL will be used if both enabled)])], , [with_gnutls="yes"] ) AC_ARG_WITH( [system-config], [AC_HELP_STRING([--with-system-config=FILE], [define gnupg system wide config])], [SYSTEM_CONFIG="${withval}"], [test "${HAVE_W32_SYSTEM}" = "yes" && SYSTEM_CONFIG="%SystemRoot%\\\\${PACKAGE}.conf" || SYSTEM_CONFIG="/etc/${PACKAGE}.conf"] ) AC_ARG_WITH( [gnupg-home], [AC_HELP_STRING([--with-gnupg-home=DIR], [define gnupg home])], [GNUPG_HOME="${withval}"], [test "${HAVE_W32_SYSTEM}" = "yes" && GNUPG_HOME="~\\\\.gnupg" || GNUPG_HOME="~/.gnupg"] ) AC_ARG_WITH( [libgpg-error-prefix], [AC_HELP_STRING([--with-libgpg-error-prefix=DIR], [define libgpgp-error prefix])], , [with_libgpg_error_prefix="/usr" ] ) AC_ARG_WITH( [libassuan-prefix], [AC_HELP_STRING([--with-libassuan-prefix=DIR], [define libassuan prefix])], , [with_libassuan_prefix="/usr" ] ) AC_ARG_WITH( [libgcrypt-prefix], [AC_HELP_STRING([--with-libgcrypt-prefix=DIR], [define libgcrypt prefix])], , [with_libgcrypt_prefix="/usr" ] ) if test "${HAVE_W32_SYSTEM}" = "yes"; then AC_CHECK_PROGS([MAN2HTML], [man2html]) test -z "${MAN2HTML}" && AC_MSG_ERROR([man2html is required for win32]) fi if test "${enable_pedantic}" = "yes"; then enable_strict="yes" CFLAGS="${CFLAGS} -ansi -pedantic -D__STRICT_ANSI__ -D_ISOC99_SOURCE -D_DEFAULT_SOURCE" fi if test "${enable_strict}" = "yes"; then CFLAGS="${CFLAGS} -Wall -Wextra -Wpointer-arith -Wsign-compare -Wno-unused-parameter -Wno-unused-function" fi AC_FUNC_MKTIME AC_TYPE_SIGNAL AC_FUNC_VPRINTF AC_CHECK_FUNCS([ \ gettimeofday memmove memset socket strchr strdup strerror strrchr \ snprintf timegm unsetenv \ ]) AX_PTHREAD(, [AC_MSG_ERROR([Cannot find pthreads])]) CC="${PTHREAD_CC}" AC_ARG_VAR([LIBGPG_ERROR_CFLAGS], [C compiler flags for libgpg-error]) AC_ARG_VAR([LIBGPG_ERROR_LIBS], [linker flags for libgpg-error]) if test -z "${LIBGPG_ERROR_LIBS}"; then AC_MSG_CHECKING([for libgpg-error]) if ! test -x "${with_libgpg_error_prefix}/bin/gpg-error-config"; then AC_MSG_ERROR([Cannot locate libgpg-error]) else AC_MSG_RESULT([found]) LIBGPG_ERROR_CFLAGS="`\"${with_libgpg_error_prefix}/bin/gpg-error-config\" --cflags`" LIBGPG_ERROR_LIBS="`\"${with_libgpg_error_prefix}/bin/gpg-error-config\" --libs`" fi fi AC_ARG_VAR([LIBASSUAN_CFLAGS], [C compiler flags for libassuan]) AC_ARG_VAR([LIBASSUAN_LIBS], [linker flags for libassuan]) if test -z "${LIBASSUAN_LIBS}"; then AC_MSG_CHECKING([for libassuan]) test -x "${with_libassuan_prefix}/bin/libassuan-config" || AC_MSG_ERROR([Cannot locate libassuan]) "${with_libassuan_prefix}/bin/libassuan-config" --version | grep "^2\." > /dev/null || AC_MSG_ERROR([Need assuan-2]) AC_MSG_RESULT([found]) LIBASSUAN_CFLAGS="`\"${with_libassuan_prefix}/bin/libassuan-config\" --cflags`" LIBASSUAN_LIBS="`\"${with_libassuan_prefix}/bin/libassuan-config\" --libs`" fi AC_ARG_VAR([LIBGCRYPT_CFLAGS], [C compiler flags for libgcrypt]) AC_ARG_VAR([LIBGCRYPT_LIBS], [linker flags for libgcrypt]) if test -z "${LIBGCRYPT_LIBS}"; then AC_MSG_CHECKING([for libgcrypt]) if ! test -x "${with_libgcrypt_prefix}/bin/libgcrypt-config"; then AC_MSG_ERROR([Cannot locate libgcrypt]) else AC_MSG_RESULT([found]) LIBGCRYPT_CFLAGS="`\"${with_libgcrypt_prefix}/bin/libgcrypt-config\" --cflags`" LIBGCRYPT_LIBS="`\"${with_libgcrypt_prefix}/bin/libgcrypt-config\" --libs`" fi fi PKG_CHECK_MODULES([OPENSSL], [libcrypto >= 0.9.7], [HAVE_OPENSSL="yes"], [HAVE_OPENSSL="no"]) if test "${HAVE_OPENSSL}" = "no"; then PKG_CHECK_MODULES([OPENSSL], [openssl >= 0.9.7], [HAVE_OPENSSL="yes"], [HAVE_OPENSSL="no"]) fi PKG_CHECK_MODULES([GNUTLS], [gnutls >= 1.4], [HAVE_GNUTLS="yes"], [HAVE_GNUTLS="no"]) PKG_CHECK_MODULES([PKCS11_HELPER], [libpkcs11-helper-1 >= 1.02],, [AC_MSG_ERROR([Cannot locate pkcs11-helper])]) PKCS11_HELPER_1_CHECK_FEATURES([threading token certificate engine_crypto]) AC_MSG_CHECKING([cryptographic library to use]) if test "${with_openssl}" = "yes" -a "${HAVE_OPENSSL}" != "yes"; then with_openssl="no" fi if test "${with_gnutls}" = "yes" -a "${HAVE_GNUTLS}" != "yes"; then with_gnutls="no" fi if test "${with_openssl}" = "no" -a "${with_gnutls}" = "no"; then AC_MSG_ERROR([Cannot locate OpenSSL or GNUTLS]) fi AC_SUBST([CONFIG_PROXY_SOCKET], [${with_proxy_socket}]) AC_SUBST([CONFIG_PROXY_USER], [${with_proxy_user}]) AC_SUBST([CONFIG_PROXY_USER_GROUP], [${with_proxy_user_group}]) AC_SUBST([CONFIG_PROXY_GROUP], [${with_proxy_group}]) AC_DEFINE_UNQUOTED([CONFIG_PROXY_GROUP], ["${with_proxy_group}"], [proxy group]) AM_CONDITIONAL([ENABLE_PROXY], [test "${enable_proxy}" = "yes"]) if test "${with_openssl}" = "yes"; then AC_MSG_RESULT([Using OpenSSL]) AC_DEFINE([ENABLE_OPENSSL], [1], [Use OpenSSL library]) CRYPTO_CFLAGS="${OPENSSL_CFLAGS}" CRYPTO_LIBS="${OPENSSL_LIBS}" else AC_MSG_RESULT([Using GNUTLS]) AC_DEFINE([ENABLE_GNUTLS], [1], [Use GNUTLS library]) CRYPTO_CFLAGS="${GNUTLS_CFLAGS}" CRYPTO_LIBS="${GNUTLS_LIBS}" fi AC_SUBST([CRYPTO_CFLAGS]) AC_SUBST([CRYPTO_LIBS]) if test "${HAVE_W32_SYSTEM}" = "yes"; then AC_DEFINE([CONFIG_PATH_SEPARATOR], ['\\'], [System path separator]) else AC_DEFINE([CONFIG_PATH_SEPARATOR], ['/'], [System path separator]) fi AM_CONDITIONAL([HAVE_W32_SYSTEM], [test "${HAVE_W32_SYSTEM}" = "yes"]) AC_HEADER_STDC AC_C_CONST AC_C_VOLATILE AC_C_INLINE AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_CHECK_HEADERS([ \ stdio.h \ stdlib.h \ stdargs.h \ malloc.h \ ctype.h \ string.h \ errno.h \ ]) AC_CHECK_HEADERS([ \ signal.h \ dlfcn.h \ unistd.h \ ]) AC_CHECK_HEADERS([sys/ucred.h]) AC_CHECK_DECLS( [SO_PEERCRED],,, [[ #include #include #include ]] ) AC_CHECK_DECLS( [LOCAL_PEERCRED],,, [[ #include #include #include #ifdef HAVE_SYS_UCRED_H #include #endif ]] ) AC_DEFINE_UNQUOTED([CONFIG_GPG_HOME], ["${GNUPG_HOME}"], [gnupg home]) AC_DEFINE_UNQUOTED([CONFIG_SYSTEM_CONFIG], ["${SYSTEM_CONFIG}"], [system config]) AC_CONFIG_FILES([ Makefile gnupg-pkcs11-scd-proxy/Makefile gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.service.in gnupg-pkcs11-scd/Makefile distro/Makefile distro/debian/Makefile distro/rpm/Makefile distro/rpm/gnupg-pkcs11-scd.spec ]) AC_OUTPUT gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/000077500000000000000000000000001316227662100210075ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/Makefile.am000066400000000000000000000032711316227662100230460ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # MAINTAINERCLEANFILES=$(srcdir)/Makefile.in SUBDIRS=debian rpm gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/000077500000000000000000000000001316227662100222315ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/Makefile.am000066400000000000000000000036111316227662100242660ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # SUFFIXES=.m4 MAINTAINERCLEANFILES=$(srcdir)/Makefile.in CLEANFILES=copyright BUILT_SOURCES=copyright dist_noinst_DATA= \ changelog \ README.build \ compat \ control \ copyright.m4 \ copyright \ rules \ watch .m4: $(M4) -Dtop_srcdir="$(top_srcdir)" "$^" > "$@" gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/README.build000066400000000000000000000005071316227662100242110ustar00rootroot00000000000000gnupg-pkcs11-scd for Debian --------------------------- To build gnupg-pkcs11-scd follow these steps 1) unpack the tarball 2) cd gnupg-pkcs11-scd- 3) ln -s distro/debian 4) invoke your build tool, e.g. dpkg-buildpackage -rfakeroot -- S. Wefel Mon, 29 Jan 2007 16:56:30 +0100 gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/changelog000066400000000000000000000006421316227662100241050ustar00rootroot00000000000000gnupg-pkcs11-scd (0.04-0) unstable; urgency=low * New upstream release -- Sandro Wefel (testlaeufer) Tue, 24 Apr 2007 19:12:05 +0200 gnupg-pkcs11-scd (0.03-1.0saw0) unstable; urgency=low * Initial release. - based on version gnupg-pkcs11-scd-0.03 from 2007-01-05 -- Sandro Wefel (testlaeufer) Mon, 19 Feb 2007 19:27:59 +0100 gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/compat000066400000000000000000000000021316227662100234270ustar00rootroot000000000000005 gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/control000066400000000000000000000012311316227662100236310ustar00rootroot00000000000000Source: gnupg-pkcs11-scd Priority: extra Maintainer: S. Wefel Build-Depends: cdbs (>= 0.4.27-1), debhelper (>= 5), autotools-dev, libssl-dev, pkg-config, libpkcs11-helper1-dev (>=1.03), libgpg-error-dev (>=1.3), libassuan-dev (>=0.9.2), libgcrypt11-dev (>=1.2.2) Standards-Version: 3.7.2 Section: utils Package: gnupg-pkcs11-scd Section: utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Suggests: gnupg2 (> 2.0.0-2), gpgsm Description: Enable PKCS#11 based cryptographic devices in GnuPG. gnupg-pkcs11 is a project to implement a BSD-licensed smart-card daemon to enable the use of PKCS#11 tokens with GnuPG. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/copyright.m4000066400000000000000000000010521316227662100245010ustar00rootroot00000000000000This package was debianized by S. Wefel on Mon, 29 Jan 2007 16:56:30 +0100. It was downloaded from http://gnupg-pkcs11.sourceforge.net/ Upstream Authors: Zeljko Vrba Alon Bar-Lev Copyright: Copyright (c) 2006-2007 Zeljko Vrba Copyright (c) 2006-2017 Alon Bar-Lev include(top_srcdir/COPYING) On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/BSD'. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/rules000077500000000000000000000001561316227662100233130ustar00rootroot00000000000000#!/usr/bin/make -f include /usr/share/cdbs/1/class/autotools.mk include /usr/share/cdbs/1/rules/debhelper.mk gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/debian/watch000066400000000000000000000001261316227662100232610ustar00rootroot00000000000000version=3 http://downloads.sourceforge.net/gnupg-pkcs11/gnupg-pkcs11-scd-(.*).tar.bz2 gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/rpm/000077500000000000000000000000001316227662100216055ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/rpm/Makefile.am000066400000000000000000000033151316227662100236430ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # MAINTAINERCLEANFILES=$(srcdir)/Makefile.in dist_noinst_DATA=gnupg-pkcs11-scd.spec gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/distro/rpm/gnupg-pkcs11-scd.spec.in000066400000000000000000000052561316227662100260650ustar00rootroot00000000000000%define name @PACKAGE@ %define version @VERSION@ %define release 1 Summary: GnuPG-compatible smart-card daemon with PKCS#11 support Name: %{name} Version: %{version} Release: %{release}%{dist} License: BSD Packager: Eddy Nigg Group: System/Crypto Url: http://gnupg-pkcs11.sourceforge.net Source: http://downloads.sourceforge.net/gnupg-pkcs11/%{name}-%{version}.tar.bz2 BuildRequires: openssl-devel >= 0.9.7a BuildRequires: pkcs11-helper-devel >= 1.03 BuildRequires: libassuan-devel BuildRequires: libgcrypt-devel BuildRequires: systemd Requires: openssl >= 0.9.7a Requires: pkcs11-helper >= 1.03 Requires: libassuan Requires: libgcrypt %description gnupg-pkcs11-scd is a drop-in replacement for the smart-card daemon (scd) shipped with the next-generation GnuPG (gnupg-2). The daemon interfaces to smart-cards by using RSA Security Inc. PKCS#11 Cryptographic Token Interface (Cryptoki). %package proxy Summary: GnuPG compatible smart-card proxy daemon for gnupg-pkcs11-scd Requires: %{name} = %{version}-%{release} Requires(pre): /usr/sbin/useradd Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %description proxy A proxy for gnupg-pkcs11-scd. %pre proxy getent group gnupg-pkcs11-scd-proxy >/dev/null || groupadd -r gnupg-pkcs11-scd-proxy getent group gnupg-pkcs11 >/dev/null || groupadd -r gnupg-pkcs11 getent passwd gnupg-pkcs11-scd-proxy >/dev/null || \ useradd -r -g gnupg-pkcs11-scd-proxy -G gnupg-pkcs11 -s /sbin/nologin \ -d / -c "gnupg-pkcs11-scd-proxy" gnupg-pkcs11-scd-proxy %post proxy %systemd_post gnupg-pkcs11-scd-proxy.service %preun proxy %systemd_preun gnupg-pkcs11-scd-proxy.service %postun proxy %systemd_postun gnupg-pkcs11-scd-proxy.service %prep %setup -q %build %configure \ --enable-proxy \ --with-proxy-socket=/run/gnupg-pkcs11-scd-proxy/cmd \ %{nil} %{__make} %{?_smp_mflags} %install %{__make} %{?_smp_mflags} install DESTDIR="%{?buildroot}" install -dm 755 "%{buildroot}%{_unitdir}" install -m 644 "gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.service" "%{buildroot}%{_unitdir}/" %files %{_bindir}/gnupg-pkcs11-scd %{_mandir}/man1/gnupg-pkcs11-scd.* %{_docdir}/%{name}/COPYING %{_docdir}/%{name}/README %{_docdir}/%{name}/gnupg-pkcs11-scd.conf.example %files proxy %{_bindir}/gnupg-pkcs11-scd-proxy %{_bindir}/gnupg-pkcs11-scd-proxy-server %{_mandir}/man1/gnupg-pkcs11-scd-proxy.* %{_unitdir}/gnupg-pkcs11-scd-proxy.service %changelog * Mon Jan 15 2007 Eddy Nigg - Build new version 0.03 * Mon Dec 11 2006 Eddy Nigg - Change config example to leave in the doc dir - Remove gnutls dependency * Mon Dec 4 2006 Eddy Nigg - Fix dependencies, cleanup. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/000077500000000000000000000000001316227662100236515ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/Makefile.am000066400000000000000000000047371316227662100257200ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in \ $(NULKL) CLEANFILES = \ gnupg-pkcs11-scd-proxy.1.html \ gnupg-pkcs11-scd-proxy.service \ $(NULL) SUFFIXES = .in AM_CPPFLAGS = \ -DCONFIG_PROXY_SOCKET='"@CONFIG_PROXY_SOCKET@"' \ -DCONFIG_SCD_BIN='"$(bindir)/gnupg-pkcs11-scd"' \ $(NULL) if ENABLE_PROXY bin_PROGRAMS = \ gnupg-pkcs11-scd-proxy \ gnupg-pkcs11-scd-proxy-server \ $(NULL) nodist_noinst_DATA = \ gnupg-pkcs11-scd-proxy.service \ $(NULL) dist_man_MANS = \ gnupg-pkcs11-scd-proxy.1 \ $(NULL) else EXTRA_DIST = \ gnupg-pkcs11-scd-proxy.1 \ $(NULL) endif gnupg_pkcs11_scd_proxy_SOURCES= \ gnupg-pkcs11-scd-proxy.c \ $(NULL) gnupg_pkcs11_scd_proxy_server_SOURCES= \ gnupg-pkcs11-scd-proxy-server.c \ $(NULL) .in: sed \ -e 's#@bindir_POST[@]#$(bindir)#g' \ -e 's#@CONFIG_PROXY_SOCKET_POST[@]#$(CONFIG_PROXY_SOCKET)#g' \ < $< > $@ gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy-server.c000066400000000000000000000150071316227662100314720ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_UCRED_H #include #endif static volatile int s_stop = 0; static RETSIGTYPE sigterm(int signo) { (void)signo; s_stop = 1; #if RETSIGTYPE != void return 0 #endif } static RETSIGTYPE sigchld(int signo) { int status; (void)signo; wait(&status); #if RETSIGTYPE != void return 0 #endif } static void usage(char *name) { printf ( ( "%s %s\n" "\n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions. See the file COPYING for details.\n" "\n" "Syntax: %s [options]\n" "Smartcard daemon for GnuPG\n" "\n" "Options:\n" " \n" " --socket=FILE use this socket\n" " --socket-group=GROUP set socket group\n" " --scd=FILE use this smartcard daemon\n" " --scd-config=FILE scd configuration (required)\n" " -v, --verbose verbose\n" " --log-file use a log file for the server\n" " --help print this information\n" ), PACKAGE, PACKAGE_VERSION, name ); } int main(int argc, char *argv[]) { struct sockaddr_un addr; int fd = -1; int null = -1; int ret = 1; char *socket_name = CONFIG_PROXY_SOCKET; char *scd_bin = CONFIG_SCD_BIN; char *socket_group = CONFIG_PROXY_GROUP; char *scd_config = NULL; gid_t socket_gid; enum { OPT_VERBOSE, OPT_LOG_FILE, OPT_SOCKET, OPT_SOCKET_GROUP, OPT_SCD_BIN, OPT_SCD_CONFIG, OPT_VERSION, OPT_HELP }; static struct option long_options[] = { { "verbose", no_argument, NULL, OPT_VERBOSE }, { "log-file", required_argument, NULL, OPT_LOG_FILE }, { "socket", required_argument, NULL, OPT_SOCKET }, { "socket-group", required_argument, NULL, OPT_SOCKET_GROUP }, { "scd", required_argument, NULL, OPT_SCD_BIN }, { "scd-config", required_argument, NULL, OPT_SCD_CONFIG }, { "version", no_argument, NULL, OPT_VERSION }, { "help", no_argument, NULL, OPT_HELP }, { NULL, 0, NULL, 0 } }; int opt; while ((opt = getopt_long (argc, argv, "v", long_options, NULL)) != -1) { switch (opt) { case OPT_SOCKET: socket_name = optarg; break; case OPT_SOCKET_GROUP: socket_group = optarg; break; case OPT_SCD_BIN: scd_bin = optarg; break; case OPT_SCD_CONFIG: scd_config = optarg; break; case OPT_VERBOSE: case 'v': break; case OPT_LOG_FILE: break; case OPT_VERSION: printf ( "%s %s\n" "\n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", PACKAGE, PACKAGE_VERSION ); exit (0); break; case OPT_HELP: usage(argv[0]); exit(0); break; default: fprintf(stderr, "invalid usage\n"); exit(1); break; } } if (scd_config == NULL) { fprintf(stderr, "--scd-config is missing\n"); goto cleanup; } { struct group *g = getgrnam(socket_group); if (g == NULL) { fprintf(stderr, "cannot resolve group '%s'\n", socket_group); goto cleanup; } socket_gid = g->gr_gid; } signal(SIGCHLD, sigchld); { struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = sigterm; sigaction(SIGTERM, &action, NULL); sigaction(SIGINT, &action, NULL); } if ((null = open("/dev/null", O_RDWR)) == -1) { perror("open null"); goto cleanup; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; if (strlen (socket_name) + 1 >= sizeof (addr.sun_path)) { fprintf(stderr, "Socket '%s' too long, expected %ld\n", socket_name, (long)sizeof (addr.sun_path)); goto cleanup; } strcpy(addr.sun_path, socket_name); unlink(addr.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); goto cleanup; } if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { fprintf(stderr, "Cannot bind '%s': %s\n", socket_name, strerror(errno)); goto cleanup; } if (listen(fd, SOMAXCONN) == -1) { fprintf(stderr, "Cannot listen '%s': %s\n", socket_name, strerror(errno)); goto cleanup; } if (chown(socket_name, -1, socket_gid) == -1) { fprintf(stderr, "Cannot chown '%s': %s\n", socket_name, strerror(errno)); goto cleanup; } if (chmod(socket_name, 0660) == -1) { fprintf(stderr, "Cannot chmod '%s': %s\n", socket_name, strerror(errno)); goto cleanup; } while(!s_stop) { uid_t peeruid; int accepted = -1; pid_t pid; if ((accepted = accept(fd, NULL, NULL)) == -1) { if (errno != EINTR && errno != EAGAIN) { perror("accept"); goto cleanup; } goto cleanup1; } #if HAVE_DECL_LOCAL_PEERCRED { struct xucred xucred; socklen_t len = sizeof(xucred); if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &len) == -1) { perror("getsockopt"); goto cleanup1; } if (xucred.cr_version != XUCRED_VERSION) { fprintf(stderr, "Mismatch credentials version actual %d expected %d", xucred.cr_version, XUCRED_VERSION); goto cleanup1; } peeruid = xucred.cr_uid; } #elif HAVE_DECL_SO_PEERCRED { struct ucred ucred; socklen_t len = sizeof(ucred); if (getsockopt(accepted, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { perror("getsockopt"); goto cleanup1; } peeruid = ucred.uid; } #else fprintf(stderr, "Cannot determine credentials\n"); goto cleanup; #endif if ((pid = fork()) == -1) { perror("fork"); goto cleanup; } if (pid == 0) { struct rlimit rlim; char uid_string[100]; int i; if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { perror("getrlimit"); exit(1); } sprintf(uid_string, "%d", peeruid); dup2(accepted, 0); dup2(accepted, 1); for (i = 3; i < (int)rlim.rlim_cur; i++) { close(i); } execl( scd_bin, scd_bin, "--multi-server", "--options", scd_config, "--uid-acl", uid_string, NULL ); fprintf(stderr, "Cannot execute '%s': %s\n", scd_bin, strerror(errno)); exit(1); } cleanup1: if (accepted != -1) { close(accepted); } } ret = 0; cleanup: if (null != -1) { close(null); } if (fd != -1) { close(fd); } return ret; } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.1000066400000000000000000000107741316227662100301120ustar00rootroot00000000000000.\" .\" Copyright (c) 2006-2007 Zeljko Vrba .\" Copyright (c) 2006-2017 Alon Bar-Lev .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are met: .\" .\" o Redistributions of source code must retain the above copyright notice, .\" this list of conditions and the following disclaimer. .\" o Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" o Neither the name of the nor the names of its .\" contributors may be used to endorse or promote products derived from .\" this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd October 15, 2017 .Os POSIX-compatible .Dt gnupg-pkcs11-scd-proxy 1 .Sh NAME .Nm gnupg-pkcs11-scd-proxy .Nd GnuPG-compatible smart-card proxy daemon .Nm gnupg-pkcs11-scd-proxy-server .Nd GnuPG-compatible smart-card proxy server daemon .Sh SYNOPSIS .Nm gnupg-pkcs11-scd-proxy .Op --multi-server .Op --socket Ar file .Op --verbose .Op --log-file Ar file .Op --help .Nm gnupg-pkcs11-scd-proxy-server .Op --socket Ar file .Op --socket-group Ar group .Op --scd Ar file .Op --scd-config Ar file .Op --verbose .Op --log-file Ar file .Op --help .Sh DESCRIPTION .Nm gnupg-pkcs11-scd-proxy is a drop-in replacement for the smart-card daemon (scd) shipped with the next-generation GnuPG (gnupg-2). The daemon is a proxy into .Nm gnupg-pkcs11-scd-proxy-server which can be run within different security context to load the .Nm gnupg-pkcs11-scd smart-card daemon. .Pp The communications between the proxy and the server is unix socket based. The assuan socket is created as world readable but with a specific user ACL, so that only initiating user can connect to the daemon. .Pp The recommended version of gnupg is 2.1 since in this version the .Nm gnupg-pkcs11-scd configuration does not specify any specific key information and can be used as generic for all users. .Pp The following options are available: .Bl -tag -width "AA" .It --multi-server Run in multi-server mode (foreground). In addition to communicating over stdin/stdout, the server also opens an additional listening UNIX socket. .It --socket Ar file Socket name, default should be sufficient. .It --socket-group Ar group A custom group to set for the proxy server socket, this may serve first level of access control. .It --scd Ar file The smartcard daemon location, default should be sufficient. .It --scd-config Ar file The smartcard daemon configuration, required parameter. .It --verbose Be verbose while running. .It --no-detach Do not detach from console (useful for debugging purposes). .It --log-file Ar file Output log to .Ar file . .It --help Print help information. .El .Pp When the daemon receives any of the SIGHUP, SIGTERM and SIGINT signals, it cleans up and exits. .Sh SEE ALSO .Xr gnupg-pkcs11-scd 1 .Rs .%T "gnupg-pkcs11 Home Page" .%O http://gnupg-pkcs11.sourceforge.net .Re .Sh AUTHORS AND COPYRIGHT Copyright (c) 2006-2007 Zeljko Vrba .Pp Copyright (c) 2006-2017 Alon Bar-Lev .Pp All rights reserved. .Pp THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.c000066400000000000000000000123001316227662100301570ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include static volatile int s_stop = 0; static RETSIGTYPE sigterm(int signo) { (void)signo; s_stop = 1; #if RETSIGTYPE != void return 0 #endif } static void usage(char *name) { printf ( ( "%s %s\n" "\n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions. See the file COPYING for details.\n" "\n" "Syntax: %s [options]\n" "Smartcard daemon for GnuPG\n" "\n" "Options:\n" " \n" " --multi-server run in multi server mode (foreground)\n" " --homedir specify home directory\n" " --socket=FILE use this socket\n" " -v, --verbose verbose\n" " --log-file use a log file for the server\n" " --help print this information\n" ), PACKAGE, PACKAGE_VERSION, name ); } int main(int argc, char *argv[]) { struct sockaddr_un addr; int fd = -1; int ret = 1; long on = 1; char *socket_name = CONFIG_PROXY_SOCKET; typedef struct fds_s { int fd; char *name; int peer; char buffer[1024]; size_t buffer_n; } fds_t; fds_t fds[] = { { -1, "outgoing.out", 2, {0}, 0}, { 0, "incoming.in", 0, {0}, 0}, { 1, "incoming.out", 1, {0}, 0}, }; int fds_n = sizeof(fds) / sizeof(fds[0]); int disconnect = 0; enum { OPT_MUTLI_SERVER, OPT_HOMEDIR, OPT_SOCKET, OPT_VERBOSE, OPT_LOG_FILE, OPT_VERSION, OPT_HELP }; static struct option long_options[] = { { "multi-server", no_argument, NULL, OPT_MUTLI_SERVER }, { "homedir", required_argument, NULL, OPT_HOMEDIR }, { "socket", required_argument, NULL, OPT_SOCKET }, { "verbose", no_argument, NULL, OPT_VERBOSE }, { "log-file", required_argument, NULL, OPT_LOG_FILE }, { "version", no_argument, NULL, OPT_VERSION }, { "help", no_argument, NULL, OPT_HELP }, { NULL, 0, NULL, 0 } }; int opt; while ((opt = getopt_long (argc, argv, "v", long_options, NULL)) != -1) { switch (opt) { case OPT_MUTLI_SERVER: break; case OPT_HOMEDIR: break; case OPT_SOCKET: socket_name = optarg; break; case OPT_VERBOSE: case 'v': break; case OPT_LOG_FILE: break; case OPT_VERSION: printf ( "%s %s\n" "\n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", PACKAGE, PACKAGE_VERSION ); exit (0); break; case OPT_HELP: usage(argv[0]); exit(0); break; default: fprintf(stderr, "invalid usage\n"); exit(1); break; } } signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sigterm); signal(SIGINT, sigterm); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; if (strlen (socket_name) + 1 >= sizeof (addr.sun_path)) { fprintf(stderr, "Socket '%s' too long, expected %ld\n", socket_name, (long)sizeof (addr.sun_path)); goto cleanup; } strcpy(addr.sun_path, socket_name); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); goto cleanup; } if (ioctl(fd, FIONBIO, &on) == -1) { perror("ioctl sock"); goto cleanup; } if (connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) { fprintf(stderr, "Cannot connect '%s': %s\n", socket_name, strerror(errno)); goto cleanup; } fds[0].fd = fd; while (!s_stop && !disconnect) { struct pollfd pollfds[fds_n]; int i; memset(&pollfds, 0, sizeof(pollfds)); for (i = 0; i < fds_n; i++) { fds_t *peer = &fds[fds[i].peer]; pollfds[i].fd = fds[i].fd; if (peer->buffer_n < sizeof(peer->buffer)) { pollfds[i].events |= POLLIN; } if (fds[i].buffer_n > 0) { pollfds[i].events |= POLLOUT; } } if (poll(pollfds, sizeof(pollfds) / sizeof(struct pollfd), -1) == -1) { if (errno != EINTR && errno != EAGAIN) { perror("poll"); goto cleanup; } continue; } for (i = 0; i < fds_n && !disconnect; i++) { if ((pollfds[i].revents & POLLHUP) != 0) { disconnect = 1; } if ((pollfds[i].revents & POLLERR) != 0) { fprintf(stderr, "error %s\n", fds[i].name); goto cleanup; } } for (i = 0; i < fds_n && !disconnect; i++) { if ((pollfds[i].revents & POLLIN) != 0) { fds_t *peer = &fds[fds[i].peer]; int n; if ((n = read(fds[i].fd, peer->buffer + peer->buffer_n, sizeof(peer->buffer) - peer->buffer_n)) == -1) { fprintf(stderr, "error %s read\n", fds[i].name); goto cleanup; } if (n == 0) { disconnect = 1; } peer->buffer_n += n; } if ((pollfds[i].revents & POLLOUT) != 0) { int n; if ((n = write(fds[i].fd, fds[i].buffer, fds[i].buffer_n)) == -1) { fprintf(stderr, "error %s write\n", fds[i].name); goto cleanup; } fds[i].buffer_n -= n; memmove(fds[i].buffer, fds[i].buffer + n, fds[i].buffer_n); } } } ret = 0; cleanup: if (fd != -1) { close(fd); } return ret; } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd-proxy/gnupg-pkcs11-scd-proxy.service.in.in000066400000000000000000000010111316227662100324040ustar00rootroot00000000000000# # Notice: # You should add environment file and set SCD_CONFIG # with the location of gnupg-pkcs11-scd config file. # [Unit] Description=gnupg-pkcs11-scd-proxy [Service] EnvironmentFile=/etc/default/gnupg-pkcs11-scd-proxy Type=simple ExecStart=@bindir_POST@/gnupg-pkcs11-scd-proxy-server --scd-config=${SCD_CONFIG} RuntimeDirectory=gnupg-pkcs11-scd-proxy RuntimeDirectoryMode=0750 User=@CONFIG_PROXY_USER@ Group=@CONFIG_PROXY_GROUP@ SupplementaryGroups=@CONFIG_PROXY_USER_GROUP@ [Install] WantedBy=multi-user.target gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/000077500000000000000000000000001316227662100224725ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/Makefile.am000066400000000000000000000051141316227662100245270ustar00rootroot00000000000000# # Copyright (c) 2006-2007 Zeljko Vrba # Copyright (c) 2006-2017 Alon Bar-Lev # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # o Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # o Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # o Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in \ $(NULL) CLEANFILES = \ gnupg-pkcs11-scd.1.html \ $(NULL) AM_CFLAGS = \ $(LIBGPG_ERROR_CFLAGS) \ $(LIBASSUAN_CFLAGS) \ $(LIBGCRYPT_CFLAGS) \ $(PKCS11_HELPER_CFLAGS) \ $(CRYPTO_CFLAGS) \ $(PTHREAD_CFLAGS) \ $(NULL) LDADD = \ $(LIBGPG_ERROR_LIBS) \ $(LIBASSUAN_LIBS) \ $(LIBGCRYPT_LIBS) \ $(PKCS11_HELPER_LIBS) \ $(CRYPTO_LIBS) \ $(PTHREAD_LIBS) \ $(NULL) bin_PROGRAMS = \ gnupg-pkcs11-scd \ $(NULL) dist_doc_DATA = \ gnupg-pkcs11-scd.conf.example \ $(NULL) gnupg_pkcs11_scd_SOURCES= \ common.h \ scdaemon.c \ common.h common.c \ command.h command.c \ encoding.h encoding.c \ keyutil.h keyutil.c \ dconfig.h dconfig.c \ $(NULL) if HAVE_W32_SYSTEM EXTRA_DIST = \ gnupg-pkcs11-scd.1 \ $(NULL) dist_man_MANS = nodist_html_DATA = \ gnupg-pkcs11-scd.1.html \ $(NULL) gnupg-pkcs11-scd.1.html: gnupg-pkcs11-scd.1 $(MAN2HTML) < "$^" > "$@" else dist_man_MANS = \ gnupg-pkcs11-scd.1 \ $(NULL) endif gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/command.c000066400000000000000000001111541316227662100242570ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "command.h" #include "encoding.h" #include "keyutil.h" #define _M2S(x) #x #define M2S(x) _M2S(x) /* * OpenPGP prefix * 11 * P11 * xxxxxxxx - sha1(token_id) * 1s */ #define OPENPGP_PKCS11_SERIAL "D27600012401" "11" "503131%8s" "1111" #define OPENPGP_PKCS11_SERIAL_BYTES 4 #define OPENPGP_KEY_NAME_PREFIX "OPENPGP." #define OPENPGP_SIGN 1 #define OPENPGP_ENCR 2 #define OPENPGP_AUTH 3 /** @file Implementation of assuan commands. Currently, only one card is supported, and the first one seen is used. In GnuPG, Certificate has both an ID and an associated keypar (identified by keygrip). All of these IDs are exchanged in hex-encoded form. We use displayName given by pkcs11helper (which is actually OpenSSL formatted DN from the certificate) as the certificate ID. */ static gpg_err_code_t get_cert_blob ( assuan_context_t ctx, pkcs11h_certificate_id_t cert_id, unsigned char **p_blob, size_t *p_blob_size ) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_t cert = NULL; unsigned char *blob = NULL; size_t blob_size; *p_blob = NULL; *p_blob_size = 0; if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_create ( cert_id, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &cert ) )) != GPG_ERR_NO_ERROR || (error = common_map_pkcs11_error ( pkcs11h_certificate_getCertificateBlob ( cert, NULL, &blob_size ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((blob = (unsigned char *)malloc (blob_size)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_getCertificateBlob ( cert, blob, &blob_size ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } *p_blob = blob; *p_blob_size = blob_size; blob = NULL; error = GPG_ERR_NO_ERROR; cleanup: if (cert != NULL) { pkcs11h_certificate_freeCertificate (cert); cert = NULL; } if (blob != NULL) { free (blob); blob = NULL; } return error; } static gpg_err_code_t get_cert_sexp ( assuan_context_t ctx, pkcs11h_certificate_id_t cert_id, gcry_sexp_t *p_sexp ) { gpg_err_code_t error = GPG_ERR_GENERAL; gcry_sexp_t sexp = NULL; unsigned char *blob = NULL; size_t blob_size; *p_sexp = NULL; if ( (error = get_cert_blob (ctx, cert_id, &blob, &blob_size)) != GPG_ERR_NO_ERROR || (error = keyutil_get_cert_sexp (blob, blob_size, &sexp)) != GPG_ERR_NO_ERROR ) { goto cleanup; } *p_sexp = sexp; sexp = NULL; error = GPG_ERR_NO_ERROR; cleanup: if (sexp != NULL) { gcry_sexp_release(sexp); sexp = NULL; } if (blob != NULL) { free (blob); blob = NULL; } return error; } static gpg_err_code_t get_serial_of_tokenid( pkcs11h_token_id_t tokenid, char **serial ) { gpg_err_code_t error = GPG_ERR_GENERAL; char *serialized = NULL; char *serialpart = NULL; unsigned char *digest = NULL; size_t n; *serial = NULL; if ( (error = common_map_pkcs11_error( pkcs11h_token_serializeTokenId( NULL, &n, tokenid ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((serialized = (char *)malloc(n)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ( (error = common_map_pkcs11_error( pkcs11h_token_serializeTokenId( serialized, &n, tokenid ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((digest = (unsigned char *)malloc(gcry_md_get_algo_dlen(GCRY_MD_SHA1))) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } gcry_md_hash_buffer(GCRY_MD_SHA1, digest, serialized, strlen(serialized)); /* * Take the first N bytes. */ if ((serialpart = encoding_bin2hex(digest, OPENPGP_PKCS11_SERIAL_BYTES)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ((*serial = malloc(strlen(OPENPGP_PKCS11_SERIAL) + OPENPGP_PKCS11_SERIAL_BYTES * 2 + 1)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } sprintf(*serial, OPENPGP_PKCS11_SERIAL, serialpart); error = GPG_ERR_NO_ERROR; cleanup: if (serialized != NULL) { free(serialized); serialized = NULL; } if (serialpart != NULL) { free(serialpart); serialpart = NULL; } if (digest != NULL) { free(digest); digest = NULL; } return error; } static gpg_err_code_t get_serial( assuan_context_t ctx, char **serial ) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_token_id_list_t tokens = NULL; *serial = NULL; if ( (error = common_map_pkcs11_error( pkcs11h_token_enumTokenIds( PKCS11H_ENUM_METHOD_CACHE_EXIST, &tokens ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } /* * gpg supports only single card, let's take the first. */ if (tokens != NULL) { if ((error = get_serial_of_tokenid(tokens->token_id, serial)) != GPG_ERR_NO_ERROR) { goto cleanup; } } error = GPG_ERR_NO_ERROR; cleanup: if (tokens != NULL) { pkcs11h_token_freeTokenIdList(tokens); tokens = NULL; } return error; } /** Send status lines in the format S KEYPAIRINFO S CERTINFO If certificate is issuer, we set type to 102 (useful); otherwise it is assumed that we're in posession of private key, so the type is set to 101 (trusted). The certificate ID is percent-plus escaped displayName. */ static int send_certificate_list ( assuan_context_t ctx, pkcs11h_certificate_id_list_t head, /* list head */ int is_issuer /* true if issuer certificate */ ) { cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_list_t curr_cert; for ( curr_cert = head; curr_cert != NULL; curr_cert = curr_cert->next ) { char *certid = NULL; char *key_hexgrip = NULL; char *keypairinfo = NULL; char *gpginfo = NULL; char *info_cert = NULL; gcry_sexp_t sexp = NULL; size_t ser_len; char *key_prefix = NULL; char *nameinfo = NULL; if ((error = get_cert_sexp (ctx, curr_cert->certificate_id, &sexp)) != GPG_ERR_NO_ERROR) { goto retry; } if ((key_hexgrip = keyutil_get_cert_hexgrip (sexp)) == NULL) { error = GPG_ERR_ENOMEM; goto retry; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_serializeCertificateId ( NULL, &ser_len, curr_cert->certificate_id ) )) != GPG_ERR_NO_ERROR ) { goto retry; } if ((certid = (char *)malloc (ser_len)) == NULL ) { error = GPG_ERR_ENOMEM; goto retry; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_serializeCertificateId ( certid, &ser_len, curr_cert->certificate_id ) )) != GPG_ERR_NO_ERROR ) { goto retry; } if ((info_cert = strdup (is_issuer ? "102 " : "101 ")) == NULL) { error = GPG_ERR_ENOMEM; goto retry; } if (!encoding_strappend (&info_cert, certid)) { error = GPG_ERR_ENOMEM; goto retry; } if ( data->config->openpgp_sign != NULL && !strcmp (data->config->openpgp_sign, key_hexgrip) ) { key_prefix = M2S(OPENPGP_SIGN) " "; } else if ( data->config->openpgp_encr != NULL && !strcmp (data->config->openpgp_encr, key_hexgrip) ) { key_prefix = M2S(OPENPGP_ENCR) " "; } else if ( data->config->openpgp_auth != NULL && !strcmp (data->config->openpgp_auth, key_hexgrip) ) { key_prefix = M2S(OPENPGP_AUTH) " "; } if ( (nameinfo = strdup (key_hexgrip)) == NULL || !encoding_strappend (&nameinfo, " ") || !encoding_strappend (&nameinfo, curr_cert->certificate_id->displayName) ) { error = GPG_ERR_ENOMEM; goto retry; } if ( (error = assuan_write_status ( ctx, "KEY-FRIEDNLY", nameinfo )) != GPG_ERR_NO_ERROR ) { goto retry; } if (key_prefix != NULL) { if ( (gpginfo = strdup (key_prefix)) == NULL || !encoding_strappend (&gpginfo, key_hexgrip) ) { error = GPG_ERR_ENOMEM; goto retry; } if ( (error = assuan_write_status ( ctx, "KEY-FPR", gpginfo )) != GPG_ERR_NO_ERROR ) { goto retry; } } if ( (error = assuan_write_status ( ctx, "CERTINFO", info_cert )) != GPG_ERR_NO_ERROR ) { goto retry; } /* send keypairinfo if not issuer certificate */ if(!is_issuer) { if ( (keypairinfo = strdup (key_hexgrip)) == NULL || !encoding_strappend (&keypairinfo, " ") || !encoding_strappend (&keypairinfo, certid) ) { error = GPG_ERR_ENOMEM; goto retry; } if ( (error = assuan_write_status ( ctx, "KEYPAIRINFO", keypairinfo )) != GPG_ERR_NO_ERROR ) { goto retry; } } error = GPG_ERR_NO_ERROR; retry: if (info_cert != NULL) { free (info_cert); info_cert = NULL; } if (certid != NULL) { free (certid); certid = NULL; } if (key_hexgrip != NULL) { free (key_hexgrip); key_hexgrip = NULL; } if (keypairinfo != NULL) { free (keypairinfo); keypairinfo = NULL; } if (gpginfo != NULL) { free (gpginfo); gpginfo = NULL; } if (nameinfo != NULL) { free (nameinfo); nameinfo = NULL; } if (error != GPG_ERR_NO_ERROR) { goto cleanup; } } error = GPG_ERR_NO_ERROR; cleanup: return error; } int _get_certificate_by_name (assuan_context_t ctx, char *name, int typehint, pkcs11h_certificate_id_t *p_cert_id, char **p_key) { cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); gpg_err_code_t error = GPG_ERR_BAD_KEY; pkcs11h_certificate_id_list_t user_certificates = NULL; pkcs11h_certificate_id_list_t curr_cert; pkcs11h_certificate_id_t cert_id = NULL; char *key_hexgrip = NULL; gcry_sexp_t sexp = NULL; char *key = NULL; int type; *p_cert_id = NULL; if (p_key != NULL) { *p_key = NULL; } if (name == NULL) { type = typehint; } else if ( /* gnupg-2.0 mode */ data->config->openpgp_sign != NULL || data->config->openpgp_encr != NULL || data->config->openpgp_auth != NULL ) { type = typehint; } else if (strncmp (name, OPENPGP_KEY_NAME_PREFIX, strlen (OPENPGP_KEY_NAME_PREFIX))) { return common_map_pkcs11_error ( pkcs11h_certificate_deserializeCertificateId (p_cert_id, name) ); } else { type = atoi(name + strlen (OPENPGP_KEY_NAME_PREFIX)); } switch (type) { case OPENPGP_SIGN: key = data->config->openpgp_sign; break; case OPENPGP_ENCR: key = data->config->openpgp_encr; break; case OPENPGP_AUTH: key = data->config->openpgp_auth; break; default: error = GPG_ERR_BAD_KEY; goto cleanup; } if (key == NULL) { error = GPG_ERR_BAD_KEY; goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_enumCertificateIds ( PKCS11H_ENUM_METHOD_CACHE_EXIST, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, NULL, &user_certificates ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } for ( curr_cert = user_certificates; curr_cert != NULL && cert_id == NULL; curr_cert = curr_cert->next ) { if ((error = get_cert_sexp (ctx, curr_cert->certificate_id, &sexp)) != GPG_ERR_NO_ERROR) { goto cleanup; } if ((key_hexgrip = keyutil_get_cert_hexgrip (sexp)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if (!strcmp (key_hexgrip, key)) { if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_duplicateCertificateId ( &cert_id, curr_cert->certificate_id ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } } if (cert_id == NULL) { error = GPG_ERR_BAD_KEY; goto cleanup; } *p_cert_id = cert_id; cert_id = NULL; if (p_key != NULL) { *p_key = key; } error = GPG_ERR_NO_ERROR; cleanup: if (sexp != NULL) { gcry_sexp_release(sexp); sexp = NULL; } if (key_hexgrip != NULL) { free (key_hexgrip); key_hexgrip = NULL; } if (user_certificates != NULL) { pkcs11h_certificate_freeCertificateIdList (user_certificates); user_certificates = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } return error; } void cmd_free_data (assuan_context_t ctx) { cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); if (data->data != NULL) { free (data->data); data->data = NULL; data->size = 0; } } gpg_error_t cmd_null (assuan_context_t ctx, char *line) { (void)ctx; (void)line; return gpg_error (GPG_ERR_NO_ERROR); } gpg_error_t cmd_serialno (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; char *serial = NULL; if ( (error = get_serial(ctx, &serial)) != GPG_ERR_NO_ERROR ) { goto cleanup; } if (serial != NULL) { char buffer[1024]; sprintf(buffer, "%s 0", serial); if ( (error = assuan_write_status ( ctx, "SERIALNO", buffer )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } error = GPG_ERR_NO_ERROR; cleanup: if (serial != NULL) { free(serial); serial = NULL; } return gpg_error (error); } /** TODO: handle --force option! */ gpg_error_t cmd_learn (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_list_t user_certificates = NULL; pkcs11h_certificate_id_list_t issuer_certificates = NULL; char *serial = NULL; (void)line; if ( (error = get_serial(ctx, &serial)) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = assuan_write_status ( ctx, "SERIALNO", serial )) != GPG_ERR_NO_ERROR || (error = assuan_write_status ( ctx, "APPTYPE", "PKCS11" )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_enumCertificateIds ( PKCS11H_ENUM_METHOD_CACHE_EXIST, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuer_certificates, &user_certificates ) )) != GPG_ERR_NO_ERROR || (error = send_certificate_list ( ctx, user_certificates, 0 )) != GPG_ERR_NO_ERROR || (error = send_certificate_list ( ctx, issuer_certificates, 1 )) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (issuer_certificates != NULL) { pkcs11h_certificate_freeCertificateIdList (issuer_certificates); issuer_certificates = NULL; } if (user_certificates != NULL) { pkcs11h_certificate_freeCertificateIdList (user_certificates); user_certificates = NULL; } if (serial != NULL) { free(serial); serial = NULL; } return gpg_error (error); } /** Return certificate contents. Line contains the percent-plus escaped certificate ID. */ gpg_error_t cmd_readcert (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_t cert_id = NULL; pkcs11h_certificate_t cert = NULL; unsigned char *blob = NULL; size_t blob_size; if ( (error = _get_certificate_by_name ( ctx, line, 0, &cert_id, NULL )) != GPG_ERR_NO_ERROR || (error = get_cert_blob (ctx, cert_id, &blob, &blob_size)) != GPG_ERR_NO_ERROR || (error = assuan_send_data (ctx, blob, blob_size)) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (cert != NULL) { pkcs11h_certificate_freeCertificate (cert); cert = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } if (blob != NULL) { free (blob); blob = NULL; } return gpg_error (error); } /** Read key given cert id in line. */ gpg_error_t cmd_readkey (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_t cert_id = NULL; gcry_sexp_t sexp = NULL; unsigned char *blob = NULL; size_t blob_size; if ( (error = _get_certificate_by_name ( ctx, line, 0, &cert_id, NULL )) != GPG_ERR_NO_ERROR || (error = get_cert_sexp (ctx, cert_id, &sexp)) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((blob_size = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0)) == 0) { error = GPG_ERR_BAD_KEY; goto cleanup; } if ((blob = (unsigned char *)malloc (blob_size)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if (gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, blob, blob_size) == 0) { error = GPG_ERR_BAD_KEY; goto cleanup; } if ( (error = assuan_send_data( ctx, blob, gcry_sexp_canon_len (blob, 0, NULL, NULL) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (sexp != NULL) { gcry_sexp_release(sexp); sexp = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } if (blob != NULL) { free (blob); blob = NULL; } return gpg_error (error); } /** Store hex-encoded data from line to be signed/decrypted. */ gpg_error_t cmd_setdata (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); int append = 0; int index; size_t len; while (*line != '\x0' && (isspace (*line) || *line == '-')) { if (*line == '-') { static const char *appendprm = "--append "; char *p = line; while (*line != '\x0' && !isspace (*line)) { line++; } line++; if (!strncmp (p, appendprm, strlen (appendprm))) { p += strlen (appendprm); append = 1; } } else { line++; } } if (!append) { cmd_free_data (ctx); } if (!encoding_hex2bin(line, NULL, &len)) { error = GPG_ERR_INV_DATA; goto cleanup; } if (!append) { index = 0; data->size = len; data->data = (unsigned char *)malloc(data->size); } else { index = data->size; data->size += len; data->data = (unsigned char *)realloc(data->data, data->size); } if (!encoding_hex2bin (line, data->data + index, NULL)) { error = GPG_ERR_INV_DATA; goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: return gpg_error (error); } /** Sign data (set by SETDATA) with certificate id in line. */ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line) { static const unsigned char rmd160_prefix[] = /* (1.3.36.3.2.1) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; static const unsigned char md5_prefix[] = /* (1.2.840.113549.2.5) */ { 0x30, 0x2c, 0x30, 0x09, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; static const unsigned char sha1_prefix[] = /* (1.3.14.3.2.26) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static const unsigned char sha224_prefix[] = /* (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 const unsigned char sha256_prefix[] = /* (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 const unsigned char sha384_prefix[] = /* (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 const unsigned char sha512_prefix[] = /* (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 }; gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_t cert_id = NULL; pkcs11h_certificate_t cert = NULL; cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); cmd_data_t *_data = data; int need_free__data = 0; int session_locked = 0; unsigned char *sig = NULL; size_t sig_len; char hash[100] = ""; enum { INJECT_NONE, INJECT_RMD160, INJECT_MD5, INJECT_SHA1, INJECT_SHA224, INJECT_SHA256, INJECT_SHA384, INJECT_SHA512 } inject = INJECT_NONE; if (data->data == NULL) { error = GPG_ERR_INV_DATA; goto cleanup; } while (*line != '\x0' && (isspace (*line) || *line == '-')) { if (*line == '-') { static const char *hashprm = "--hash="; char *p = line; while (*line != '\x0' && !isspace (*line)) { line++; } line++; if (!strncmp (p, hashprm, strlen (hashprm))) { p += strlen (hashprm); *(line-1) = '\0'; snprintf (hash, sizeof(hash), "%s", p); } } else { line++; } } if (*line == '\x0') { error = GPG_ERR_INV_DATA; goto cleanup; } /* * sender prefixed data with algorithm OID */ if (strcmp(hash, "")) { if (!strcmp(hash, "rmd160") && data->size == (0x14 + sizeof(rmd160_prefix)) && !memcmp (data->data, rmd160_prefix, sizeof (rmd160_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "rmd160") && data->size == 0x14) { inject = INJECT_RMD160; } else if (!strcmp(hash, "md5") && data->size == (0x10 + sizeof(md5_prefix)) && !memcmp (data->data, md5_prefix, sizeof (md5_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "md5") && data->size == 0x10) { inject = INJECT_MD5; } else if (!strcmp(hash, "sha1") && data->size == (0x14 + sizeof(sha1_prefix)) && !memcmp (data->data, sha1_prefix, sizeof (sha1_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "sha1") && data->size == 0x14) { inject = INJECT_SHA1; } else if (!strcmp(hash, "sha224") && data->size == (0x1c + sizeof(sha224_prefix)) && !memcmp (data->data, sha224_prefix, sizeof (sha224_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "sha224") && data->size == 0x1c) { inject = INJECT_SHA224; } else if (!strcmp(hash, "sha256") && data->size == (0x20 + sizeof(sha256_prefix)) && !memcmp (data->data, sha256_prefix, sizeof (sha256_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "sha256") && data->size == 0x20) { inject = INJECT_SHA256; } else if (!strcmp(hash, "sha384") && data->size == (0x30 + sizeof(sha384_prefix)) && !memcmp (data->data, sha384_prefix, sizeof (sha384_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "sha384") && data->size == 0x30) { inject = INJECT_SHA384; } else if (!strcmp(hash, "sha512") && data->size == (0x40 + sizeof(sha512_prefix)) && !memcmp (data->data, sha512_prefix, sizeof (sha512_prefix))) { inject = INJECT_NONE; } else if (!strcmp(hash, "sha512") && data->size == 0x40) { inject = INJECT_SHA512; } else { common_log (LOG_DEBUG, "unsupported hash algo (hash=%s,size=%d)", hash, data->size); error = GPG_ERR_UNSUPPORTED_ALGORITHM; goto cleanup; } } else { if ( data->size == 0x10 + sizeof (md5_prefix) || data->size == 0x14 + sizeof (sha1_prefix) || data->size == 0x14 + sizeof (rmd160_prefix) ) { if ( memcmp (data->data, md5_prefix, sizeof (md5_prefix)) && memcmp (data->data, sha1_prefix, sizeof (sha1_prefix)) && memcmp (data->data, rmd160_prefix, sizeof (rmd160_prefix)) ) { error = GPG_ERR_UNSUPPORTED_ALGORITHM; goto cleanup; } } else { /* * unknown hash algorithm; * gnupg's scdaemon forces to SHA1 */ inject = INJECT_SHA1; } } if (inject != INJECT_NONE) { const unsigned char *oid; size_t oid_size; switch (inject) { case INJECT_RMD160: oid = rmd160_prefix; oid_size = sizeof (rmd160_prefix); break; case INJECT_MD5: oid = md5_prefix; oid_size = sizeof (md5_prefix); break; case INJECT_SHA1: oid = sha1_prefix; oid_size = sizeof (sha1_prefix); break; case INJECT_SHA224: oid = sha224_prefix; oid_size = sizeof (sha224_prefix); break; case INJECT_SHA256: oid = sha256_prefix; oid_size = sizeof(sha256_prefix); break; case INJECT_SHA384: oid = sha384_prefix; oid_size = sizeof(sha384_prefix); break; case INJECT_SHA512: oid = sha512_prefix; oid_size = sizeof(sha512_prefix); break; default: error = GPG_ERR_INV_DATA; goto cleanup; } need_free__data = 1; if ((_data = (cmd_data_t *)malloc (sizeof (cmd_data_t))) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ((_data->data = (unsigned char *)malloc (data->size + oid_size)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } _data->size = 0; memmove (_data->data+_data->size, oid, oid_size); _data->size += oid_size; memmove (_data->data+_data->size, data->data, data->size); _data->size += data->size; } if ( (error = _get_certificate_by_name ( ctx, line, OPENPGP_SIGN, &cert_id, NULL )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_create ( cert_id, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &cert ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_lockSession (cert) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } session_locked = 1; if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_signAny ( cert, CKM_RSA_PKCS, _data->data, _data->size, NULL, &sig_len ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((sig = (unsigned char *)malloc (sig_len)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_signAny ( cert, CKM_RSA_PKCS, _data->data, _data->size, sig, &sig_len ) )) != GPG_ERR_NO_ERROR || (error = assuan_send_data(ctx, sig, sig_len)) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (session_locked) { pkcs11h_certificate_releaseSession (cert); session_locked = 0; } if (cert != NULL) { pkcs11h_certificate_freeCertificate (cert); cert = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } if (sig != NULL) { free (sig); sig = NULL; } if (need_free__data) { free (_data->data); _data->data = NULL; free (_data); _data = NULL; } return gpg_error (error); } /** Decrypt data (set by SETDATA) with certificate id in line. */ gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_t cert_id = NULL; pkcs11h_certificate_t cert = NULL; unsigned char *ptext = NULL; size_t ptext_len; int session_locked = 0; cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); cmd_data_t _data; if ( data == NULL || data->data == NULL ) { error = GPG_ERR_INV_DATA; goto cleanup; } /* * Guess.. taken from openpgp card implementation * and java PKCS#11 provider. */ _data.data = data->data; _data.size = data->size; if ( *_data.data == 0 && ( _data.size == 129 || _data.size == 193 || _data.size == 257 || _data.size == 385 || _data.size == 513 ) ) { _data.data++; _data.size--; } if ( (error = _get_certificate_by_name ( ctx, line, OPENPGP_ENCR, &cert_id, NULL )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_create ( cert_id, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &cert ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_lockSession (cert) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } session_locked = 1; if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_decryptAny ( cert, CKM_RSA_PKCS, _data.data, _data.size, NULL, &ptext_len ) )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((ptext = (unsigned char *)malloc (ptext_len)) == NULL) { error = GPG_ERR_ENOMEM; goto cleanup; } if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_decryptAny ( cert, CKM_RSA_PKCS, _data.data, _data.size, ptext, &ptext_len ) )) != GPG_ERR_NO_ERROR || (error = assuan_write_status(ctx, "PADDING", "0")) != GPG_ERR_NO_ERROR || (error = assuan_send_data(ctx, ptext, ptext_len)) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (session_locked) { pkcs11h_certificate_releaseSession (cert); session_locked = 0; } if (cert != NULL) { pkcs11h_certificate_freeCertificate (cert); cert = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } if (ptext != NULL) { free (ptext); ptext = NULL; } return gpg_error (error); } /** pkcs11-helper neither supports getting random data, nor exports sufficient data to use raw PKCS#11. */ gpg_error_t cmd_random (assuan_context_t ctx, char *line) { (void)ctx; (void)line; return gpg_error (GPG_ERR_INV_OP); } /** Not implemented. */ gpg_error_t cmd_checkpin (assuan_context_t ctx, char *line) { (void)ctx; (void)line; return gpg_error (GPG_ERR_INV_OP); } gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx); gpg_err_code_t error = GPG_ERR_GENERAL; if (!strcmp (line, "version")) { char *s = PACKAGE_VERSION; error = assuan_send_data(ctx, s, strlen (s)); } else if (!strcmp (line, "pid")) { char buf[50]; snprintf (buf, sizeof (buf), "%lu", (unsigned long)getpid()); error = assuan_send_data(ctx, buf, strlen (buf)); } else if (!strcmp (line, "socket_name")) { const char *s = data->socket_name; if (s == NULL) { error = GPG_ERR_INV_DATA; } else { error = assuan_send_data(ctx, s, strlen (s)); } } else if (!strcmp (line, "status")) { pkcs11h_certificate_id_list_t user_certificates = NULL; char flag = 'r'; if ( common_map_pkcs11_error ( pkcs11h_certificate_enumCertificateIds ( PKCS11H_ENUM_METHOD_CACHE_EXIST, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, NULL, &user_certificates ) ) == GPG_ERR_NO_ERROR ) { if (user_certificates != NULL) { flag = 'u'; pkcs11h_certificate_freeCertificateIdList (user_certificates); user_certificates = NULL; } } error = assuan_send_data(ctx, &flag, 1); } else if (!strcmp (line, "reader_list")) { error = GPG_ERR_NO_DATA; } else { error = GPG_ERR_INV_DATA; } return gpg_error (error); } gpg_error_t cmd_restart (assuan_context_t ctx, char *line) { (void)ctx; (void)line; return gpg_error (GPG_ERR_NO_ERROR); } gpg_error_t cmd_getattr (assuan_context_t ctx, char *line) { pkcs11h_certificate_id_list_t user_certificates = NULL; char *serial = NULL; gpg_err_code_t error = GPG_ERR_GENERAL; if (!strcmp (line, "SERIALNO")) { if ( (error = get_serial(ctx, &serial)) != GPG_ERR_NO_ERROR ) { goto cleanup; } if (serial != NULL) { if ( (error = assuan_write_status ( ctx, "SERIALNO", serial )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } } else if (!strcmp (line, "KEY-FPR")) { if ( (error = common_map_pkcs11_error ( pkcs11h_certificate_enumCertificateIds ( PKCS11H_ENUM_METHOD_CACHE_EXIST, ctx, PKCS11H_PROMPT_MASK_ALLOW_ALL, NULL, &user_certificates ) )) != GPG_ERR_NO_ERROR || (error = send_certificate_list ( ctx, user_certificates, 0 )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } else if (!strcmp (line, "CHV-STATUS")) { if ( (error = assuan_write_status( ctx, "CHV-STATUS", "1 1 1 1 1 1 1" )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } else if (!strcmp (line, "DISP-NAME")) { if ( (error = assuan_write_status( ctx, "DISP-NAME", "PKCS#11" )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } else if (!strcmp (line, "KEY-ATTR")) { int i; for (i=0;i<3;i++) { char buffer[1024]; /* I am not sure 2048 is right here... */ snprintf(buffer, sizeof(buffer), "%d 1 %u %u %d", i+1, GCRY_PK_RSA, 2048, 0); if ( (error = assuan_write_status( ctx, "KEY-ATTR", buffer )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } } else if (!strcmp (line, "EXTCAP")) { int i; for (i=0;i<3;i++) { char buffer[1024]; /* I am not sure what these are... */ snprintf(buffer, sizeof(buffer), "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d", 0, 0, 0, 0, 2048, 0, 0); if ( (error = assuan_write_status( ctx, "EXTCAP", buffer )) != GPG_ERR_NO_ERROR ) { goto cleanup; } } } else { error = GPG_ERR_INV_DATA; goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (user_certificates != NULL) { pkcs11h_certificate_freeCertificateIdList (user_certificates); user_certificates = NULL; } if (serial != NULL) { free(serial); serial = NULL; } return gpg_error (error); } gpg_error_t cmd_setattr (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; if (!strncmp (line, "CHV-STATUS-1 ", 13)) { } else { error = GPG_ERR_INV_DATA; goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: return gpg_error (error); } gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { gpg_err_code_t error = GPG_ERR_GENERAL; pkcs11h_certificate_id_t cert_id = NULL; gcry_mpi_t n_mpi = NULL; gcry_mpi_t e_mpi = NULL; unsigned char *n_hex = NULL; unsigned char *e_hex = NULL; char *n_resp = strdup ("n "); char *e_resp = strdup ("e "); unsigned char *blob = NULL; char *serial = NULL; char *key = NULL; size_t blob_size; char timestamp[100] = {0}; while (*line != '\x0' && !isdigit (*line)) { if (*line == '-') { static const char *ts = "--timestamp="; char *p = line; while (*line != '\x0' && !isspace (*line)) { line++; } line++; if (!strncmp (p, ts, strlen (ts))) { p += strlen (ts); sprintf (timestamp, "%d", (int)isotime2epoch (p)); } } else { line++; } } if (*line == '\x0') { error = GPG_ERR_INV_DATA; goto cleanup; } if (strlen (timestamp) == 0) { sprintf (timestamp, "%d", (int)time (NULL)); } if ( (error = _get_certificate_by_name ( ctx, NULL, atoi(line), &cert_id, &key )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = assuan_write_status ( ctx, "KEY-FPR", key )) != GPG_ERR_NO_ERROR || (error = assuan_write_status( ctx, "KEY-CREATED-AT", timestamp )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ((error = get_serial_of_tokenid(cert_id->token_id, &serial)) != GPG_ERR_NO_ERROR) { goto cleanup; } if ( (error = assuan_write_status ( ctx, "SERIALNO", serial )) != GPG_ERR_NO_ERROR || (error = get_cert_blob ( ctx, cert_id, &blob, &blob_size )) != GPG_ERR_NO_ERROR || (error = keyutil_get_cert_mpi ( blob, blob_size, &n_mpi, &e_mpi )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( gcry_mpi_aprint ( GCRYMPI_FMT_HEX, &n_hex, NULL, n_mpi ) || gcry_mpi_aprint ( GCRYMPI_FMT_HEX, &e_hex, NULL, e_mpi ) ) { error = GPG_ERR_BAD_KEY; goto cleanup; } if ( !encoding_strappend (&n_resp, (char *)n_hex) || !encoding_strappend (&e_resp, (char *)e_hex) ) { error = GPG_ERR_ENOMEM; goto cleanup; } if ( (error = assuan_write_status( ctx, "KEY-DATA", n_resp )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( (error = assuan_write_status( ctx, "KEY-DATA", e_resp )) != GPG_ERR_NO_ERROR ) { goto cleanup; } error = GPG_ERR_NO_ERROR; cleanup: if (n_mpi != NULL) { gcry_mpi_release (n_mpi); n_mpi = NULL; } if (e_mpi != NULL) { gcry_mpi_release (e_mpi); e_mpi = NULL; } if (n_hex != NULL) { gcry_free (n_hex); n_hex = NULL; } if (e_hex != NULL) { gcry_free (e_hex); e_hex = NULL; } if (n_resp != NULL) { free (n_resp); n_resp = NULL; } if (e_resp != NULL) { free (e_resp); e_resp = NULL; } if (blob != NULL) { free (blob); blob = NULL; } if (cert_id != NULL) { pkcs11h_certificate_freeCertificateId (cert_id); cert_id = NULL; } if (serial != NULL) { free(serial); serial = NULL; } return gpg_error (error); } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/command.h000066400000000000000000000054761316227662100242750ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __COMMAND_H #define __COMMAND_H #include "dconfig.h" typedef struct { dconfig_data_t *config; const char *socket_name; unsigned char *data; size_t size; } cmd_data_t; void cmd_free_data (assuan_context_t ctx); gpg_error_t cmd_null (assuan_context_t ctx, char *line); gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line); gpg_error_t cmd_serialno (assuan_context_t ctx, char *line); gpg_error_t cmd_learn (assuan_context_t ctx, char *line); gpg_error_t cmd_readcert (assuan_context_t ctx, char *line); gpg_error_t cmd_readkey (assuan_context_t ctx, char *line); gpg_error_t cmd_setdata (assuan_context_t ctx, char *line); gpg_error_t cmd_pksign (assuan_context_t ctx, char *line); gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line); gpg_error_t cmd_random (assuan_context_t ctx, char *line); gpg_error_t cmd_checkpin (assuan_context_t ctx, char *line); gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line); gpg_error_t cmd_restart (assuan_context_t ctx, char *line); gpg_error_t cmd_genkey (assuan_context_t ctx, char *line); gpg_error_t cmd_getattr (assuan_context_t ctx, char *line); gpg_error_t cmd_setattr (assuan_context_t ctx, char *line); #endif gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/common.c000066400000000000000000000062611316227662100241330ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include static FILE *log_stream = NULL; void common_set_log_stream (FILE *log) { log_stream = log; } FILE * common_get_log_stream (void) { return log_stream; } void common_vlog ( common_log_t class, const char * const format, va_list args ) { unsigned id; #if defined(HAVE_W32_SYSTEM) id = 0; #else id = (unsigned)pthread_self (); #endif if (log_stream != NULL) { fprintf (log_stream, "%s[%u.%u]: ", PACKAGE, (unsigned)getpid (), id); vfprintf (log_stream, format, args); fputc ('\n', log_stream); fflush (log_stream); if (class == LOG_FATAL) { exit (1); } } } void common_log ( common_log_t class, const char * const format, ... ) { if (log_stream != NULL) { va_list args; va_start (args, format); common_vlog (class, format, args); va_end (args); } } gpg_err_code_t common_map_pkcs11_error (int rv) { gpg_err_code_t error; switch (rv) { case CKR_OK: error = GPG_ERR_NO_ERROR; break; case CKR_PIN_LOCKED: error = GPG_ERR_PIN_BLOCKED; break; case CKR_PIN_INCORRECT: error = GPG_ERR_BAD_PIN; break; case CKR_DEVICE_REMOVED: error = GPG_ERR_CARD_REMOVED; break; case CKR_KEY_TYPE_INCONSISTENT: error = GPG_ERR_WRONG_PUBKEY_ALGO; break; case CKR_KEY_FUNCTION_NOT_PERMITTED: error = GPG_ERR_WRONG_KEY_USAGE; break; case CKR_MECHANISM_INVALID: error = GPG_ERR_UNSUPPORTED_ALGORITHM; break; case CKR_CANCEL: error = GPG_ERR_CANCELED; break; default: error = GPG_ERR_CARD; break; } return error; } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/common.h000066400000000000000000000047641316227662100241460ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __COMMON_H #define __COMMON_H #include "config.h" #include #include #include #ifdef HAVE_MALLOC_H #include #endif #include #include #include #include #include #if defined(HAVE_W32_SYSTEM) #include #else #include #endif #if defined(USE_VALGRIND) #include "valgrind/memcheck.h" #endif typedef enum { LOG_DEBUG=0, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL } common_log_t; gpg_err_code_t common_map_pkcs11_error (int rv); gpg_err_code_t common_map_assuan_error (int err); void common_set_log_stream (FILE *log); FILE * common_get_log_stream (void); void common_vlog ( common_log_t class, const char * const format, va_list args ); void common_log ( common_log_t class, const char * const format, ... ); #endif gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/dconfig.c000066400000000000000000000145571316227662100242630ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "dconfig.h" static void trim (char * const line) { char *p; if ((p = strchr (line, '#')) != NULL) { *p = '\x0'; } p = line; while (*p != '\x0') { if (*p == '\t' || *p == '\r' || *p == '\n') { *p = ' '; } p++; } p = line; while (*p != '\x0' && *p == ' ') { p++; } memmove (line, p, strlen (p)+1); p = line + strlen (line) - 1; while (p > line && *p == ' ') { *p = '\x0'; p--; } } static int prefix_is (const char * const s, const char * const p) { return !strncmp (s, p, strlen (p)); } int dconfig_read (const char * const _file, dconfig_data_t * const config) { #if defined(HAVE_W32_SYSTEM) char file[1024]; #else const char *file = _file; #endif char line[1024]; FILE *fp = NULL; int ok = 0; memset (config, 0, sizeof (dconfig_data_t)); config->pin_cache = PKCS11H_PIN_CACHE_INFINITE; #if defined(HAVE_W32_SYSTEM) if (!ExpandEnvironmentStrings (_file, file, sizeof (file))) { goto cleanup; } #endif if ((fp = fopen (file, "r")) == NULL) { common_log (LOG_ERROR, "Cannot open configuration file '%s'", file); goto cleanup; } while (fgets (line, sizeof (line), fp) != NULL) { trim (line); if (!strcmp (line, "")) { } else if (prefix_is (line, "log-file ")) { char *p = strchr (line, ' '); trim (p); config->log_file = strdup (p); } else if (!strcmp (line, "verbose")) { config->verbose = 1; } else if (!strcmp (line, "debug-all")) { config->debug = 1; } else if (prefix_is (line, "providers ")) { char *p = strchr (line, ' '); char *p2; int entry = 0; while (entry < DCONFIG_MAX_PROVIDERS && (p2 = strchr (p, ',')) != NULL) { *p2 = '\x0'; trim (p); if (strlen (p) > 0) { config->providers[entry++].name = strdup (p); } p = p2+1; } if (entry < DCONFIG_MAX_PROVIDERS) { trim (p); if (strlen (p) > 0) { config->providers[entry++].name = strdup (p); } } } else if (prefix_is (line, "pin-cache ")) { config->pin_cache = atoi (strchr (line, ' ')); } else if (prefix_is (line, "openpgp-sign")) { char *p = strchr (line, ' '); trim (p); config->openpgp_sign = strdup (p); } else if (prefix_is (line, "openpgp-encr")) { char *p = strchr (line, ' '); trim (p); config->openpgp_encr = strdup (p); } else if (prefix_is (line, "openpgp-auth")) { char *p = strchr (line, ' '); trim (p); config->openpgp_auth = strdup (p); } else if (prefix_is (line, "provider-")) { char *name = strchr (line, '-')+1; char *p; if ((p = strchr (name, '-')) != NULL) { int entry; *p = '\x0'; p++; entry = 0; while ( entry < DCONFIG_MAX_PROVIDERS && config->providers[entry].name != NULL && strcmp (config->providers[entry].name, name) ) { entry++; } if (entry < DCONFIG_MAX_PROVIDERS) { if (prefix_is (p, "library ")) { char *p2 = strchr (p, ' ') + 1; trim (p2); config->providers[entry].library = strdup (p2); } else if (!strcmp (p, "allow-protected-auth")) { config->providers[entry].allow_protected = 1; } else if (prefix_is (p, "private-mask ")) { char *p2 = strchr (p, ' ') + 1; trim (p2); sscanf (p2, "%x", &config->providers[entry].private_mask); } else if (!strcmp (p, "cert-private")) { config->providers[entry].cert_is_private = 1; } else { common_log (LOG_ERROR, "Invalid certificate attribute '%s'", p); goto cleanup; } } } } else { common_log (LOG_ERROR, "Invalid option '%s'", line); goto cleanup; } } ok = 1; cleanup: if (fp != NULL) { fclose (fp); fp = NULL; } if (!ok) { dconfig_free (config); } return ok; } void dconfig_print (const dconfig_data_t * const config) { int entry; common_log (LOG_DEBUG, "config: debug=%d, verbose=%d", config->debug, config->verbose); common_log (LOG_DEBUG, "config: pin_cache=%d", config->pin_cache); for (entry = 0;entry < DCONFIG_MAX_PROVIDERS;entry++) { if (config->providers[entry].name != NULL) { common_log (LOG_DEBUG, "config: provider: name=%s, library=%s, allow_protected=%d, cert_is_private=%d, private_mask=%08x", config->providers[entry].name, config->providers[entry].library, config->providers[entry].allow_protected, config->providers[entry].cert_is_private, config->providers[entry].private_mask); } } } void dconfig_free (dconfig_data_t * const config) { #define f(x) do { \ if (x != NULL) { \ free (x); \ x = NULL; \ } \ } while (0) int i; f (config->log_file); f (config->openpgp_sign); f (config->openpgp_encr); f (config->openpgp_auth); for (i=0;iproviders->name); f (config->providers->library); } #undef f } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/dconfig.h000066400000000000000000000043301316227662100242540ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __DCONFIG_H #define __DCONFIG_H #define DCONFIG_MAX_PROVIDERS 20 typedef struct { char *log_file; int debug; int verbose; int pin_cache; char *openpgp_sign; char *openpgp_encr; char *openpgp_auth; struct { char *name; char *library; int allow_protected; int cert_is_private; unsigned private_mask; } providers[DCONFIG_MAX_PROVIDERS]; } dconfig_data_t; int dconfig_read (const char * const file, dconfig_data_t * const config); void dconfig_print (const dconfig_data_t * const config); void dconfig_free (dconfig_data_t * const config); #endif gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/encoding.c000066400000000000000000000123141316227662100244250ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "encoding.h" /* p_target must be free () */ int encoding_hex2bin ( const char * const source, unsigned char * const target, size_t * const p_target_size ) { const char *ps = source; unsigned char *pt = target; char buf[3] = {'\0', '\0', '\0'}; int i = 0; if (p_target_size != NULL) { *p_target_size = 0; } while (*ps != '\x0') { if (isxdigit ((unsigned char)*ps)) { buf[i%2] = *ps; if ((i%2) == 1) { if (pt != NULL) { unsigned v; if (sscanf (buf, "%x", &v) != 1) { v = 0; } *pt = (char)(v & 0xff); pt++; } if (p_target_size != NULL) { (*p_target_size)++; } } i++; } ps++; } return 1; } /* return string must be free() */ char * encoding_bin2hex ( const unsigned char * const source, const size_t source_size ) { static const char *x = "0123456789ABCDEF"; char * target = NULL; size_t i; if ((target = (char *)malloc (source_size*2+1)) != NULL) { for (i=0;i>4]; target[i*2+1] = x[(source[i]&0x0f)>>0]; } target[source_size*2] = '\x0'; } return target; } /* p_str must by dynamic allocated */ int encoding_strappend ( char * * const p_str, char *s ) { char *p = (char *)realloc (*p_str, strlen (*p_str)+strlen(s)+1); if (p == NULL) { return 0; } *p_str = p; strcat (*p_str, s); return 1; } /* From gnupg */ /* timegm() is a GNU function that might not be available everywhere. It's basically the inverse of gmtime() - you give it a struct tm, and get back a time_t. It differs from mktime() in that it handles the case where the struct tm is UTC and the local environment isn't. Note, that this replacement implementaion is not thread-safe! Some BSDs don't handle the putenv("foo") case properly, so we use unsetenv if the platform has it to remove environment variables. */ #ifndef HAVE_TIMEGM static char old_zone[1024]; time_t timegm (struct tm *tm) { time_t answer; char *zone; zone=getenv("TZ"); putenv("TZ=UTC"); tzset(); answer=mktime(tm); if(zone) { if (strlen (old_zone) == 0) { snprintf(old_zone, sizeof(old_zone), "TZ=%s", zone); old_zone[sizeof(old_zone)-1] = '\0'; } putenv (old_zone); } else { #ifdef HAVE_UNSETENV unsetenv("TZ"); #else putenv("TZ"); #endif } tzset(); return answer; } #endif /*!HAVE_TIMEGM*/ time_t isotime2epoch ( const char * const string ) { const char *s; int year, month, day, hour, minu, sec; struct tm tmbuf; int i; if (!*string) return (time_t)(-1); for (s=string, i=0; i < 8; i++, s++) if (!isdigit (*s)) return (time_t)(-1); if (*s != 'T') return (time_t)(-1); for (s++, i=9; i < 15; i++, s++) if (!isdigit (*s)) return (time_t)(-1); if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ',')) return (time_t)(-1); /* Wrong delimiter. */ year = (string[0]-'0') * 1000 + (string[1]-'0') * 100 + (string[2]-'0') * 10 + (string[3]-'0') * 1; month = (string[4]-'0') * 10 + (string[5]-'0'); day = (string[6]-'0') * 10 + (string[7]-'0'); hour = (string[9]-'0') * 10 + (string[10]-'0'); minu = (string[11]-'0') * 10 + (string[12]-'0'); sec = (string[13]-'0') * 10 + (string[14]-'0'); /* Basic checks. */ if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minu > 59 || sec > 61 ) return (time_t)(-1); memset (&tmbuf, 0, sizeof tmbuf); tmbuf.tm_sec = sec; tmbuf.tm_min = minu; tmbuf.tm_hour = hour; tmbuf.tm_mday = day; tmbuf.tm_mon = month-1; tmbuf.tm_year = year - 1900; tmbuf.tm_isdst = -1; return timegm (&tmbuf); } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/encoding.h000066400000000000000000000040341316227662100244320ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __ENCODING_H #define __ENCODING_H int encoding_hex2bin ( const char * const source, unsigned char * const target, size_t * const p_target_size ); char * encoding_bin2hex ( const unsigned char * const source, const size_t source_size ); int encoding_strappend ( char * * const p_str, char *s ); time_t isotime2epoch ( const char * const string ); #endif /* COMMON_H__ */ gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/gnupg-pkcs11-scd.1000066400000000000000000000275421316227662100255550ustar00rootroot00000000000000.\" .\" Copyright (c) 2006-2007 Zeljko Vrba .\" Copyright (c) 2006-2017 Alon Bar-Lev .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are met: .\" .\" o Redistributions of source code must retain the above copyright notice, .\" this list of conditions and the following disclaimer. .\" o Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" o Neither the name of the nor the names of its .\" contributors may be used to endorse or promote products derived from .\" this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd October 15, 2017 .Os POSIX-compatible .Dt gnupg-pkcs11-scd 1 .Sh NAME .Nm gnupg-pkcs11-scd .Nd GnuPG-compatible smart-card daemon with PKCS#11 support .Sh SYNOPSIS .Nm gnupg-pkcs11-scd .Op --server .Op --multi-server .Op --daemon .Op --homedir Ar dir .Op --uid-acl Ar uid .Op --verbose .Op --quiet .Op --sh .Op --csh .Op --options Ar file .Op --no-detach .Op --log-file Ar file .Op --help .Sh DESCRIPTION .Nm gnupg-pkcs11-scd is a drop-in replacement for the smart-card daemon (scd) shipped with the next-generation GnuPG (gnupg-2). The daemon interfaces to smart-cards by using RSA Security Inc. PKCS#11 Cryptographic Token Interface (Cryptoki). .Pp The interface with GnuPG is restricted to feching existing keys from the card. Neither new key generation nor key transfer is possible through this interface. Instead, when the smart-card is asked to generate a key in a particular slot, the existing public key in that slot is returned. This facilitates the transfer of keys on the smart-card to usage as a subkey on an existing GnuPG master key. See the GNUPG INTEGRATION section for example usage. .Pp The following options are available: .Bl -tag -width "AA" .It --server Run in server mode (foreground). If not redirected, input and output are over stdin/stdout. .It --multi-server Run in multi-server mode (foreground). In addition to communicating over stdin/stdout, the server also opens an additional listening UNIX socket. .It --daemon Detach and run in background. .It --homedir Ar dir Use this home directory instead of guessing. .It --uid-acl Ar uid Create unix socket as world read/write and apply access control that accepts only remote processes of this uid. Usable for proxy scenario. .It --verbose Be verbose while running. .It --quiet Be as quiet as possible. .It --sh Output sh-style environment variable definition. .It --csh Output csh-style environment variable definition. .It --options Ar file Read options from .Ar file . Some of the configuration options can only be set in the configuration file (see the .Sx CONFIGURATION section). .It --no-detach Do not detach from console (useful for debugging purposes). .It --log-file Ar file Output log to .Ar file . .It --help Print help information. .El .Pp When the daemon receives any of the SIGHUP, SIGTERM and SIGINT signals, it cleans up and exits. .Pp .Nm gnupg-pkcs11-scd works only with .Em already personalized cards , and supports (for the time being) only RSA keypairs. The following constraints must be satisfied: .Pp .Bl -enum -compact .It For each private key object, a certificate object must exist on the card. The existence of the corresponding public key object is not important (since the certificate includes public key). .It The certificate and the corresponding private key must have identical CKA_ID attribute. .El .Pp The PKCS#11 implementation is not obliged to enforce any of the above rules. However, practice has shown that popular PKCS#11 implementations found "in the wild" seem to respect them. .Sh NOTES Unlike gpg-agent, .Nm gnupg-pkcs11-scd supports more than one token available at the same time. In order to make gpg-agent happy, .Nm gnupg-pkcs11-scd always returns the same card serial number to gpg-agent. When unavailable token is requested, .Nm gnupg-pkcs11-scd will use NEEDPIN callback in order to ask for the requested token. When and if gpg-agent will support more than one serial number or NEEDTOKEN callback, this behavior will be modified. .Sh ENVIRONMENT .Bl -tag -width "USERPROFILE" -compact .It HOME Used to locate the home directory. .It GNUPGHOME Used instead of .Pa ~/.gnupg . .It USERPROFILE Used only on Win32 to locate the home directory. .It GNUPG_PKCS11_SOCKETDIR Create sockets in this directory, default to TMPDIR. .El .Pp Additionally, the \\\\Software\\\\GNU\\\\GnuPG\\\\HomeDir registry key is used on Win32 to locate the default GNUPGHOME. .Sh FILES Files affecting the operation of .Nm gnupg-pkcs11-scd : .Bl -tag .It Pa ~/.gnupg/gnupg-pkcs11-scd.conf .Nm gnupg-pkcs11-scd uses this as a default configuration file. .It Pa /etc/gnupg-pkcs11-scd.conf .Nm gnupg-pkcs11-scd uses this as a default system wide configuration file. .It Pa ~/.gnupg/gpg-agent.conf Default configuration file for gpg-agent. .El .Sh CONFIGURATION To tell gpg-agent to use another smart-card daemon, the following needs to be put in .Pa ~/.gnupg/gpg-agent.conf : .Bd -literal -offset indent scdaemon-program /usr/bin/gnupg-pkcs11-scd pinentry-program /usr/bin/pinentry-qt .Ed .Pp The first line is mandatory in order to use .Nm gnupg-pkcs11-scd . With the second line you can set your preferred pinentry program (it has to be one compatible with GnuPG). Of course, you need to adjust the paths according to your system setup. .Pp An example .Pa ~/.gnupg/gnupg-pkcs11-scd.conf file (lines beginning with # are comments): .Bd -literal -offset indent # Log file. #log-file log1 # Default is not verbose. #verbose # Default is no debugging. #debug-all # Pin cache period in seconds; default is infinite. #pin-cache 20 # Comma-separated list of available provider names. Then set # attributes for each provider using the provider-[name]-attribute # syntax. providers p1 # Provider attributes (see below for detailed description) provider-p1-library /usr/lib/pkcs11/p1.so #provider-p1-allow-protected-auth #provider-p1-cert-private #provider-p1-private-mask 0 # The following are for gnupg-2.0 mode #openpgp-sign 5C661B8C07CFD957F7D98D5B9A0F31D236BFAC2A #openpgp-encr D2DC0BD1EDD185969748B6025B452816F97CBA57 #openpgp-auth A7B8C1A3A8F71FCEC018886F8767927B9C8D871F .Ed .Pp The following attributes can be set for each provider: .Bl -tag -width "AA" .It library Full path to the PKCS#11 shared library (= provider). .It allow-protected-auth Allow protected authentication for provider. This needs to be supported by the provider and you should have appropriate reader hardware. .It cert-private Authentication is required before certificates can be accessed. Most configurations store certificates as public, so there is no need to use this option. .It private-mask Private key mask mode. Use this only when you have problem using private key operations. The value is hex encoded mask number. .Bl -tag -width "RECOVER" -compact .It 0 Determine automatically. .It 1 Force sign. .It 2 Force sign with recovery. .It 4 Force decrypt. .It 8 Force decrypt with unwrap. .El .It openpgp-sign [gnupg-2.0] Hex string (Upper letter, no space) SHA1 of signing public key see GNUPG INTEGRATION how to obtain. .It openpgp-encr [gnupg-2.0] Hex string (Upper letter, no space) SHA1 of encryption public key see GNUPG INTEGRATION how to obtain. .It openpgp-auth [gnupg-2.0] Hex string (Upper letter, no space) SHA1 of authentication public key see GNUPG INTEGRATION how to obtain. .El .Sh GNUPG INTEGRATION Typical steps to set up a card for gpgsm usage: .Bl -enum .It Import the CA certificate of your issuer: .Dl gpgsm --import < ca-certificate You should also manually import all self-signed certificates. .It Instruct GnuPG to discover all useful certificates on the card: .Dl gpgsm --learn-card .El .Pp Signing, verification, etc. work as usual with gpgsm. .Pp Typical steps to set up a card for gpg-2.0 usage: .Bl -enum .It Aquire key ids: .Dl gpg-agent --server gpg-connect-agent Enter "SCD LEARN" and look for "KEY-FRIEDNLY" responses, the first field is the hash, the second is the subject name. .It Instruct GnuPG to discover all useful information of card: .Dl gpg --card-status You should see valid card status. .It Now, you should virtual generate keys, the keys are not actually generated, but returned to gpg to be registered. .Dl gpg --card-edit .Dl admin .Dl generate (DO NOT BACKUP KEYS) Kill gpg-agent and modify configuration to have sign, encrypt, authenticate key hex ids. .It Alternatively, you can add the existing keys as subkeys on an existing GPG master key: .Dl gpg --edit-key MASTER_KEY_ID .Dl addcardkey .It In order to reattach a key to smartcard, remove secret key using: .Dl gpg --delete-secret-keys KEY_ID Then regenerate but without replace keys using: .Dl gpg --card-edit .Dl admin .Dl generate (DO NOT GENERATE KEYS) .El .Pp Signing, verification, etc. work as usual with gpg. .Pp Typical steps to set up a card for >=gpg-2.1.19 usage: .Bl -enum .It Refresh local key store: .Dl gpg --card-status .It Aquire key ids: .Dl gpg-agent --server gpg-connect-agent Enter "SCD LEARN" and look for "KEY-FRIEDNLY" responses, the first field is the keygrip, the second is the subject name. .It Create master key based on existing key using: .Dl gpg --expert --full-generate-key Select: .Dl (13) Existing key Enter keygrip to be used as primary key. .It Continue as usual to setup your primary key, you should probably use signature for master key. .It Add subkey using: .Dl gpg --expert --edit-key ${MASTER_KEY_ID} .Dl gpg> addkey .Dl (13) Existing key Enter keygrip to be used as subkey. .It Continue as usual to setup your subkey. .El .Pp Signing, verification, etc. work as usual with gpg. .Sh SECURITY CONSIDERATIONS All communication between components is currently unprotected and in plain text (that's how the Assuan protocol operates). It is trivial to trace (using e.g. the .Xr strace 1 program) individual components (e.g. pinentry) and steal sensitive data (such as the smart-card PIN) or even change it (e.g. the hash to be signed). .Pp When using the software in production scenario, .Sy be sure to turn off debugging/verbose options in configuration of all components. Otherwise, some sensitive data might be displayed on the screen (most notably, the PIN). .Sh SEE ALSO .Xr strace 1 .Xr truss 1 .Xr gnupg 7 .Rs .%T "GnuPG Home Page" .%O http://www.gnupg.org .Re .Rs .%T "gnupg-pkcs11 Home Page" .%O http://gnupg-pkcs11.sourceforge.net .Re .Sh AUTHORS AND COPYRIGHT Copyright (c) 2006-2007 Zeljko Vrba .Pp Copyright (c) 2006-2017 Alon Bar-Lev .Pp All rights reserved. .Pp THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/gnupg-pkcs11-scd.conf.example000066400000000000000000000025331316227662100277650ustar00rootroot00000000000000# Log file. #log-file log1 # Default is not verbose. #verbose # Default is no debugging. #debug-all # Pin cache period in seconds; default is infinite. #pin-cache 20 # Comma-separated list of available provider names. Then set # attributes for each provider using the provider-[name]-attribute # syntax. providers p1 # The following attributes can be set for each provider: # # library # Full path to the PKCS#11 shared library (= provider). # allow-protected-auth # Allow protected authentication for provider. This needs to be supported by # the provider and you should have appropriate reader hardware. # cert-private # Authentication is required before certificates can be accessed. Most # configurations store certificates as public, so there is no need to use this # option. # private-mask # Private key mask mode. Use this only when you have problem using # private key operations. The value is hex encoded mask number. # 0 Determine automatically. # 1 Force sign. # 2 Force sign with recovery. # 4 Force decrypt. # 8 Force decrypt with unwrap. provider-p1-library /usr/lib/pkcs11/p1.so #provider-p1-allow-protected-auth #provider-p1-cert-private #provider-p1-private-mask 0 #emulate-openpgpg #openpgp-sign 5C661B8C07CFD957F7D98D5B9A0F31D236BFAC2A #openpgp-encr D2DC0BD1EDD185969748B6025B452816F97CBA57 #openpgp-auth A7B8C1A3A8F71FCEC018886F8767927B9C8D871F gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/keyutil.c000066400000000000000000000146121316227662100243300ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #if defined(ENABLE_GNUTLS) #include #endif #if defined(ENABLE_OPENSSL) #include #endif #include "encoding.h" #include "keyutil.h" #if defined(ENABLE_OPENSSL) #if OPENSSL_VERSION_NUMBER < 0x00908000L typedef unsigned char *my_openssl_d2i_t; #else typedef const unsigned char *my_openssl_d2i_t; #endif #endif gpg_err_code_t keyutil_get_cert_mpi ( unsigned char *der, size_t len, gcry_mpi_t *p_n_mpi, gcry_mpi_t *p_e_mpi ) { gpg_err_code_t error = GPG_ERR_GENERAL; gcry_mpi_t n_mpi = NULL; gcry_mpi_t e_mpi = NULL; #if defined(ENABLE_GNUTLS) gnutls_x509_crt_t cert = NULL; gnutls_datum_t datum = {der, len}; gnutls_datum_t m = {NULL, 0}, e = {NULL, 0}; #elif defined(ENABLE_OPENSSL) X509 *x509 = NULL; EVP_PKEY *pubkey = NULL; char *n_hex = NULL, *e_hex = NULL; #endif *p_n_mpi = NULL; *p_e_mpi = NULL; #if defined(ENABLE_GNUTLS) if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) { cert = NULL; error = GPG_ERR_ENOMEM; goto cleanup; } if (gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) { error = GPG_ERR_BAD_CERT; goto cleanup; } if (gnutls_x509_crt_get_pk_rsa_raw (cert, &m, &e) != GNUTLS_E_SUCCESS) { error = GPG_ERR_BAD_KEY; m.data = NULL; e.data = NULL; goto cleanup; } if ( gcry_mpi_scan(&n_mpi, GCRYMPI_FMT_USG, m.data, m.size, NULL) || gcry_mpi_scan(&e_mpi, GCRYMPI_FMT_USG, e.data, e.size, NULL) ) { error = GPG_ERR_BAD_KEY; goto cleanup; } #elif defined(ENABLE_OPENSSL) if (!d2i_X509 (&x509, (my_openssl_d2i_t *)&der, len)) { error = GPG_ERR_BAD_CERT; goto cleanup; } if ((pubkey = X509_get_pubkey (x509)) == NULL) { error = GPG_ERR_BAD_CERT; goto cleanup; } if (pubkey->type != EVP_PKEY_RSA) { error = GPG_ERR_WRONG_PUBKEY_ALGO; goto cleanup; } n_hex = BN_bn2hex (pubkey->pkey.rsa->n); e_hex = BN_bn2hex (pubkey->pkey.rsa->e); if(n_hex == NULL || e_hex == NULL) { error = GPG_ERR_BAD_KEY; goto cleanup; } if ( gcry_mpi_scan (&n_mpi, GCRYMPI_FMT_HEX, n_hex, 0, NULL) || gcry_mpi_scan (&e_mpi, GCRYMPI_FMT_HEX, e_hex, 0, NULL) ) { error = GPG_ERR_BAD_KEY; goto cleanup; } #else #error Invalid configuration. #endif *p_n_mpi = n_mpi; n_mpi = NULL; *p_e_mpi = e_mpi; e_mpi = NULL; error = GPG_ERR_NO_ERROR; cleanup: if (n_mpi != NULL) { gcry_mpi_release (n_mpi); n_mpi = NULL; } if (e_mpi != NULL) { gcry_mpi_release (e_mpi); e_mpi = NULL; } #if defined(ENABLE_GNUTLS) if (m.data != NULL) { gnutls_free (m.data); m.data = NULL; } if (e.data != NULL) { gnutls_free (e.data); e.data = NULL; } if (cert != NULL) { gnutls_x509_crt_deinit (cert); cert = NULL; } #elif defined(ENABLE_OPENSSL) if (x509 != NULL) { X509_free (x509); x509 = NULL; } if (pubkey != NULL) { EVP_PKEY_free(pubkey); pubkey = NULL; } if (n_hex != NULL) { OPENSSL_free (n_hex); n_hex = NULL; } if (e_hex != NULL) { OPENSSL_free (e_hex); e_hex = NULL; } #else #error Invalid configuration. #endif return error; } /** Convert X.509 RSA public key into gcrypt internal sexp form. Only RSA public keys are accepted at the moment. The resul is stored in *sexp, which must be freed (using ) when not needed anymore. *sexp must be NULL on entry, since it is overwritten. */ gpg_err_code_t keyutil_get_cert_sexp ( unsigned char *der, size_t len, gcry_sexp_t *p_sexp ) { gpg_err_code_t error = GPG_ERR_GENERAL; gcry_mpi_t n_mpi = NULL; gcry_mpi_t e_mpi = NULL; gcry_sexp_t sexp = NULL; if ( (error = keyutil_get_cert_mpi ( der, len, &n_mpi, &e_mpi )) != GPG_ERR_NO_ERROR ) { goto cleanup; } if ( gcry_sexp_build ( &sexp, NULL, "(public-key (rsa (n %m) (e %m)))", n_mpi, e_mpi ) ) { error = GPG_ERR_BAD_KEY; goto cleanup; } *p_sexp = sexp; sexp = NULL; error = GPG_ERR_NO_ERROR; cleanup: if (n_mpi != NULL) { gcry_mpi_release (n_mpi); n_mpi = NULL; } if (e_mpi != NULL) { gcry_mpi_release (e_mpi); e_mpi = NULL; } if (sexp != NULL) { gcry_sexp_release (sexp); sexp = NULL; } return error; } #if 0 /** Calculate certid for the certificate. The certid is stored as hex-encoded, null-terminated string into certid which must be at least 41 bytes long. This is very primitive ID, just using the SHA1 of the whole certificate DER encoding. Currently not used. */ void cert_get_hexgrip(unsigned char *der, size_t len, char *certid) { int ret; char grip[20]; SHA1(der, len, grip); ret = bin2hex(hexgrip, 41, grip, 20); g_assert(ret == 20); } #endif /** Calculate hex-encoded keygrip of public key in sexp. */ char *keyutil_get_cert_hexgrip (gcry_sexp_t sexp) { char *ret = NULL; unsigned char grip[20]; if (gcry_pk_get_keygrip (sexp, grip)) { ret = encoding_bin2hex (grip, sizeof (grip)); } return ret; } gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/keyutil.h000066400000000000000000000037251316227662100243400ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __KEYUTIL_H #define __KEYUTIL_H gpg_err_code_t keyutil_get_cert_mpi ( unsigned char *der, size_t len, gcry_mpi_t *p_n_mpi, gcry_mpi_t *p_e_mpi ); gpg_err_code_t keyutil_get_cert_sexp ( unsigned char *der, size_t len, gcry_sexp_t *p_sexp ); char *keyutil_get_cert_hexgrip (gcry_sexp_t sexp); #endif gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/gnupg-pkcs11-scd/scdaemon.c000066400000000000000000000700051316227662100244310ustar00rootroot00000000000000/* * Copyright (c) 2006-2007 Zeljko Vrba * Copyright (c) 2006-2017 Alon Bar-Lev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * o Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** @file Main command loop for scdaemon. For compatibility with GnuPG's scdaemon, all command-line options are silently ignored. @todo True daemon mode and multi-server mode are not yet implemented. Only one card is currently supported. Client notification of card status change is not implemented. */ #include "common.h" #include "command.h" #include "dconfig.h" #include #include #include #include #if !defined(HAVE_W32_SYSTEM) #include #include #include #include #include #ifdef HAVE_SYS_UCRED_H #include #endif #endif #if defined(USE_GNUTLS) #include #endif #ifdef HAVE_W32_SYSTEM typedef void *gnupg_fd_t; #define GNUPG_INVALID_FD ((void*)(-1)) #define INT2FD(s) ((void *)(s)) #define FD2INT(h) ((unsigned int)(h)) #else typedef int gnupg_fd_t; #define GNUPG_INVALID_FD (-1) #define INT2FD(s) (s) #define FD2INT(h) (h) #endif typedef enum { ACCEPT_THREAD_STOP, ACCEPT_THREAD_CLEAN } accept_command_t; struct global_s; #if !defined(HAVE_W32_SYSTEM) typedef struct thread_list_s { struct thread_list_s *next; int fd; pthread_t thread; int stopped; struct global_s *global; } *thread_list_t; #endif typedef struct global_s { dconfig_data_t config; char *socket_name; #if !defined(HAVE_W32_SYSTEM) thread_list_t *threads; char *socket_dir; int fd_accept_terminate[2]; uid_t uid_acl; #endif } global_t; #if !defined(HAVE_W32_SYSTEM) static int s_parent_pid = -1; #endif #define ALARM_INTERVAL 10 #define SOCKET_DIR_TEMPLATE ( PACKAGE ".XXXXXX" ) /** Register commands with assuan. */ static int register_commands (const assuan_context_t ctx) { static struct { const char *name; assuan_handler_t handler; const char * const help; } table[] = { { "SERIALNO", cmd_serialno, NULL }, { "LEARN", cmd_learn, NULL }, { "READCERT", cmd_readcert, NULL }, { "READKEY", cmd_readkey, NULL }, { "KEY-DATA", NULL, NULL }, { "SETDATA", cmd_setdata, NULL }, { "PKSIGN", cmd_pksign, NULL }, { "PKAUTH", NULL, NULL }, { "PKDECRYPT", cmd_pkdecrypt, NULL }, { "INPUT", NULL, NULL }, { "OUTPUT", NULL, NULL }, { "GETATTR", cmd_getattr, NULL }, { "SETATTR", cmd_setattr, NULL }, { "WRITECERT", NULL, NULL }, { "WRITEKEY", NULL, NULL }, { "GENKEY", cmd_genkey, NULL }, { "RANDOM", NULL, NULL }, { "PASSWD", NULL, NULL }, { "CHECKPIN", cmd_null, NULL }, { "LOCK", NULL, NULL }, { "UNLOCK", NULL, NULL }, { "GETINFO", cmd_getinfo, NULL }, { "RESTART", cmd_restart, NULL }, { "DISCONNECT", cmd_null, NULL }, { "APDU", NULL, NULL }, { "CHV-STATUS-1", cmd_null, NULL }, /* gnupg-1.X */ { NULL, NULL, NULL } }; int i, ret; for(i=0; table[i].name; i++) { if ( (ret = assuan_register_command ( ctx, table[i].name, table[i].handler, table[i].help )) ) { return ret; } } assuan_set_hello_line (ctx, "PKCS#11 smart-card server for GnuPG ready"); /*assuan_register_reset_notify(ctx, reset_notify);*/ /*assuan_register_option_handler(ctx, option_handler);*/ return 0; } /** Command handler (single-threaded). If fd == -1, this is a pipe server, otherwise fd is UNIX socket fd to which client connected. */ static void command_handler (global_t *global, const int fd) { assuan_context_t ctx = NULL; cmd_data_t data; int ret; if (fd != -1 && global->uid_acl != (uid_t)-1) { uid_t peeruid = -1; #if HAVE_DECL_LOCAL_PEERCRED struct xucred xucred; socklen_t len = sizeof(xucred); if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &len) == -1) { common_log (LOG_WARNING, "Cannot get socket credentials: %s", strerror (errno)); goto cleanup; } if (xucred.cr_version != XUCRED_VERSION) { common_log (LOG_WARNING, "Mismatch credentials version actual %d expected %d", xucred.cr_version, XUCRED_VERSION); goto cleanup; } peeruid = xucred.cr_uid; #elif HAVE_DECL_SO_PEERCRED struct ucred ucred; socklen_t len = sizeof(ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { common_log (LOG_WARNING, "Cannot get socket credentials: %s", strerror (errno)); goto cleanup; } peeruid = ucred.uid; #endif if (peeruid != global->uid_acl) { common_log (LOG_WARNING, "Mismatch credentials actual %d expected %d", peeruid, global->uid_acl); goto cleanup; } } memset (&data, 0, sizeof (data)); data.config = &global->config; data.socket_name = global->socket_name; if ((ret = assuan_new(&ctx)) != 0) { common_log (LOG_ERROR,"failed to create assuan context %s", gpg_strerror (ret)); goto cleanup; } if(fd < 0) { assuan_fd_t fds[2] = {INT2FD(0), INT2FD(1)}; ret = assuan_init_pipe_server (ctx, fds); } else { ret = assuan_init_socket_server (ctx, INT2FD(fd), ASSUAN_SOCKET_SERVER_ACCEPTED); } if (ret != 0) { common_log (LOG_ERROR,"failed to initialize server: %s", gpg_strerror (ret)); goto cleanup; } if(((ret = register_commands(ctx))) != 0) { common_log (LOG_ERROR,"failed to register assuan commands: %s", gpg_strerror (ret)); goto cleanup; } if (global->config.verbose) { assuan_set_log_stream (ctx, common_get_log_stream()); } assuan_set_pointer (ctx, &data); while (1) { common_log (LOG_DEBUG, "accepting connection"); if ((ret = assuan_accept (ctx)) == -1) { break; } if (ret != 0) { common_log (LOG_WARNING,"assuan_accept failed: %s", gpg_strerror(ret)); break; } common_log (LOG_DEBUG, "processing connection"); if ((ret = assuan_process (ctx)) != 0) { common_log (LOG_WARNING,"assuan_process failed: %s", gpg_strerror(ret)); } common_log (LOG_DEBUG, "post-processing connection"); } cleanup: common_log (LOG_DEBUG, "cleanup connection"); if (ctx != NULL) { cmd_free_data (ctx); assuan_release (ctx); ctx = NULL; } } #if !defined(HAVE_W32_SYSTEM) static void server_socket_close (global_t *global, const int fd) { if (fd != -1) { assuan_sock_close (fd); } if (global->socket_name != NULL) { unlink (global->socket_name); free (global->socket_name); global->socket_name = NULL; } if (global->socket_dir != NULL) { rmdir (global->socket_dir); free (global->socket_dir); global->socket_dir = NULL; } assuan_sock_deinit(); } static void server_socket_create_name (global_t *global) { char *socketdir = getenv("GNUPG_PKCS11_SOCKETDIR"); if (socketdir == NULL) { socketdir = getenv("TMPDIR"); } if (socketdir == NULL) { socketdir = "/tmp"; } if ((global->socket_dir = malloc(strlen(socketdir) + strlen(SOCKET_DIR_TEMPLATE) + 100)) == NULL) { common_log (LOG_FATAL, "malloc"); } sprintf(global->socket_dir, "%s/%s", socketdir, SOCKET_DIR_TEMPLATE); if (mkdtemp (global->socket_dir) == NULL) { common_log (LOG_FATAL, "Cannot mkdtemp"); } if ((global->socket_name = (char *)malloc (strlen (global->socket_dir) + 100)) == NULL) { common_log (LOG_FATAL, "Cannot malloc"); } sprintf (global->socket_name, "%s/agent.S", global->socket_dir); } static int server_socket_create (global_t *global) { struct sockaddr_un serv_addr; int fd = -1; int rc = -1; if ((rc = assuan_sock_init()) != 0) { common_log (LOG_ERROR,"Cannot init socket %s", gpg_strerror (rc)); goto cleanup; } memset (&serv_addr, 0, sizeof (serv_addr)); serv_addr.sun_family = AF_UNIX; assert (strlen (global->socket_name) + 1 < sizeof (serv_addr.sun_path)); strcpy (serv_addr.sun_path, global->socket_name); if ((fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0)) == -1) { common_log (LOG_ERROR, "Cannot create socket", global->socket_name); goto cleanup; } if ((rc = assuan_sock_bind (fd, (struct sockaddr*)&serv_addr, sizeof (serv_addr))) == -1) { common_log (LOG_ERROR, "Cannot bing to socket '%s'", global->socket_name); goto cleanup; } #if !defined(HAVE_W32_SYSTEM) if (global->uid_acl != (uid_t)-1) { if (chmod(global->socket_name, 0666) == -1) { common_log (LOG_ERROR, "Cannot chmod '%s'", global->socket_name); goto cleanup; } if (chmod(global->socket_dir, 0755) == -1) { common_log (LOG_ERROR, "Cannot chmod '%s'", global->socket_dir); goto cleanup; } } #endif if ((rc = listen (fd, SOMAXCONN)) == -1) { common_log (LOG_ERROR, "Cannot listen to socket '%s'", global->socket_name); goto cleanup; } rc = 0; cleanup: if (rc != 0) { server_socket_close (global, fd); common_log (LOG_FATAL, "Cannot handle socket"); } common_log (LOG_INFO, "Listening to socket '%s'", global->socket_name); return fd; } static void * _server_socket_command_handler (void *arg) { thread_list_t entry = (thread_list_t)arg; accept_command_t clean = ACCEPT_THREAD_CLEAN; command_handler (entry->global, entry->fd); entry->stopped = 1; if (write (entry->global->fd_accept_terminate[1], &clean, sizeof (clean)) == -1) { common_log (LOG_FATAL, "write failed"); } return NULL; } static void * _server_socket_accept (void *arg) { thread_list_t _entry = (thread_list_t)arg; global_t *global = _entry->global; int fd = _entry->fd; thread_list_t thread_list_head = NULL; int rc = 0; free (_entry); _entry = NULL; if (pipe (global->fd_accept_terminate) == -1) { common_log (LOG_FATAL, "pipe failed"); } while (rc != -1) { fd_set fdset; FD_ZERO (&fdset); FD_SET (global->fd_accept_terminate[0], &fdset); FD_SET (fd, &fdset); rc = select (FD_SETSIZE, &fdset, NULL, NULL, NULL); if (rc != -1 && rc != 0) { if (FD_ISSET (global->fd_accept_terminate[0], &fdset)) { accept_command_t cmd; if ( (rc = read ( global->fd_accept_terminate[0], &cmd, sizeof (cmd)) ) == sizeof (cmd) ) { if (cmd == ACCEPT_THREAD_STOP) { common_log (LOG_DEBUG, "Thread command terminate"); rc = -1; } else if (cmd == ACCEPT_THREAD_CLEAN) { thread_list_t entry = thread_list_head; thread_list_t prev = NULL; common_log (LOG_DEBUG, "Cleaning up closed thread"); while (entry != NULL) { if (entry->stopped) { thread_list_t temp = entry; common_log (LOG_DEBUG, "Cleaning up closed thread1"); pthread_join (entry->thread, NULL); close (entry->fd); if (prev == NULL) { thread_list_head = entry->next; } else { prev->next = entry->next; } entry = entry->next; free (temp); } else { prev = entry; entry = entry->next; } } } } } else if (FD_ISSET (fd, &fdset)) { struct sockaddr_un addr; socklen_t addrlen = sizeof (addr); int fd2; if ((rc = fd2 = accept (fd, (struct sockaddr *)&addr, &addrlen)) != -1) { thread_list_t entry = NULL; common_log (LOG_DEBUG, "Accepted new socket connection"); if ((entry = (thread_list_t)malloc (sizeof (struct thread_list_s))) == NULL) { common_log (LOG_FATAL, "malloc failed"); } memset (entry, 0, sizeof (struct thread_list_s)); entry->next = thread_list_head; entry->fd = fd2; entry->global = global; thread_list_head = entry; if ( pthread_create ( &entry->thread, NULL, _server_socket_command_handler, entry ) ) { common_log (LOG_FATAL, "pthread failed"); } } } } } common_log (LOG_DEBUG, "Cleaning up threads"); while (thread_list_head != NULL) { thread_list_t entry = thread_list_head; thread_list_head = thread_list_head->next; common_log (LOG_DEBUG, "Cleaning up thread1"); close (entry->fd); pthread_join (entry->thread, NULL); free (entry); } return NULL; } static void server_socket_accept (global_t *global, const int fd, pthread_t *thread) { thread_list_t entry = malloc (sizeof (struct thread_list_s)); memset (entry, 0, sizeof (struct thread_list_s)); entry->fd = fd; entry->global = global; if (pthread_create (thread, NULL, _server_socket_accept, (void *)entry)) { common_log (LOG_FATAL, "pthread failed"); } } static void server_socket_accept_terminate (global_t *global, pthread_t thread) { accept_command_t stop = ACCEPT_THREAD_STOP; if (write (global->fd_accept_terminate[1], &stop, sizeof (stop)) == -1) { common_log (LOG_FATAL, "write failed"); } pthread_join (thread, NULL); close (global->fd_accept_terminate[0]); close (global->fd_accept_terminate[1]); } #endif /* HAVE_W32_SYSTEM */ static void pkcs11_log_hook ( void * const data, const unsigned flags, const char * const fmt, va_list args ) { (void)data; (void)flags; common_vlog (LOG_INFO, fmt, args); } static PKCS11H_BOOL pkcs11_token_prompt_hook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry ) { char cmd[1024]; unsigned char *user_read = NULL; size_t user_read_len = 0; assuan_context_t ctx = user_data; int rc; int ret = FALSE; (void)global_data; (void)retry; snprintf ( cmd, sizeof(cmd), "NEEDPIN Please insert token '%s' !!!DO NOT ENTER PIN HERE!!!!", token->display ); if ((rc = assuan_inquire (ctx, cmd, &user_read, &user_read_len, 1024))) { common_log (LOG_WARNING, "Token inquire error: %d", rc); goto cleanup; } if (!strcmp ((char *)user_read, "cancel")) { goto cleanup; } ret = TRUE; cleanup: if (user_read != NULL) { memset (user_read, 0, strlen ((char *)user_read)); free (user_read); user_read = NULL; } return ret; } static PKCS11H_BOOL pkcs11_pin_prompt_hook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry, char * const pin, const size_t max_pin ) { char cmd[1024]; assuan_context_t ctx = user_data; unsigned char *pin_read = NULL; size_t pin_len; int rc; int ret = FALSE; (void)global_data; snprintf ( cmd, sizeof(cmd), "NEEDPIN PIN required for token '%s' (try %u)", token->display, retry ); if ((rc = assuan_inquire (ctx, cmd, &pin_read, &pin_len, 1024))) { common_log (LOG_WARNING,"PIN inquire error: %d", rc); goto cleanup; } if (pin_len==0 || (pin_len+1 > max_pin)) { goto cleanup; } strcpy (pin, (char *)pin_read); ret = TRUE; cleanup: if (pin_read != NULL) { memset (pin_read, 0, strlen ((char *)pin_read)); free (pin_read); pin_read = NULL; } return ret; } #if !defined(HAVE_W32_SYSTEM) static RETSIGTYPE on_alarm (int signo) { (void)signo; if (s_parent_pid != -1 && kill (s_parent_pid, 0) == -1) { kill (getpid (), SIGTERM); } signal (SIGALRM, on_alarm); alarm (ALARM_INTERVAL); #if RETSIGTYPE != void return 0 #endif } static RETSIGTYPE on_signal (int signo) { (void)signo; /* * This is the only way to notify * assuan to return from its main loop... */ close (0); close (1); #if RETSIGTYPE != void return 0 #endif } #endif /* HAVE_W32_SYSTEM */ static void usage (const char * const argv0) { printf ( ( "%s %s\n" "\n" "Copyright (c) 2006-2007 Zeljko Vrba \n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions. See the file COPYING for details.\n" "\n" "Syntax: %s [options]\n" "Smartcard daemon for GnuPG\n" "\n" "Options:\n" " \n" " --server run in server mode (foreground)\n" " --multi-server run in multi server mode (foreground)\n" " --daemon run in daemon mode (background)\n" " -v, --verbose verbose\n" " -q, --quiet be somewhat more quiet\n" " -s, --sh sh-style command output\n" " -c, --csh csh-style command output\n" " --options read options from file\n" " --no-detach do not detach from the console\n" " --homedir specify home directory\n" " --uid-acl accept only this uid, implies world read/write socket\n" " --log-file use a log file for the server\n" " --help print this information\n" ), PACKAGE, PACKAGE_VERSION, argv0 ); } static char *get_home_dir (void) { #if defined(HAVE_W32_SYSTEM) static const char * GPG_HOME_KEY = "Software\\GNU\\GnuPG"; const char *HOME_ENV = getenv ("USERPROFILE"); #else const char *HOME_ENV = getenv ("HOME"); #endif char *home_dir = NULL; if (home_dir == NULL && getenv ("GNUPGHOME") != NULL) { home_dir=strdup (getenv ("GNUPGHOME")); } #if defined(HAVE_W32_SYSTEM) if (home_dir == NULL) { char key_val[1024]; HKEY hkey = NULL; DWORD dw = 0; if (RegOpenKeyEx (HKEY_CURRENT_USER, GPG_HOME_KEY, 0, KEY_READ, &hkey) != ERROR_SUCCESS) { if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPG_HOME_KEY, 0, KEY_READ, &hkey) != ERROR_SUCCESS) { hkey = NULL; } } if (hkey != NULL) { if ( RegQueryValueEx ( hkey, "HomeDir", NULL, NULL, (PBYTE)key_val, &dw ) == ERROR_SUCCESS ) { home_dir = strdup (key_val); } } if (hkey != NULL) { RegCloseKey (hkey); } } #endif if (home_dir == NULL) { if ( CONFIG_GPG_HOME[0] == '~' && HOME_ENV != NULL ) { if ((home_dir=(char *)malloc (strlen (CONFIG_GPG_HOME) + strlen (HOME_ENV))) == NULL) { common_log (LOG_FATAL, "malloc failed"); } sprintf (home_dir, "%s%s", HOME_ENV, CONFIG_GPG_HOME+1); } else { home_dir = strdup (CONFIG_GPG_HOME); } } return home_dir; } int main (int argc, char *argv[]) { enum { OPT_SERVER, OPT_MUTLI_SERVER, OPT_DAEMON, OPT_VERBOSE, OPT_QUIET, OPT_SH, OPT_CSH, OPT_OPTIONS, OPT_NO_DETACH, OPT_HOMEDIR, OPT_UID_ACL, OPT_LOG_FILE, OPT_VERSION, OPT_HELP }; static struct option long_options[] = { { "server", no_argument, NULL, OPT_SERVER }, { "multi-server", no_argument, NULL, OPT_MUTLI_SERVER }, { "daemon", no_argument, NULL, OPT_DAEMON }, { "verbose", no_argument, NULL, OPT_VERBOSE }, { "quiet", no_argument, NULL, OPT_QUIET }, { "sh", no_argument, NULL, OPT_SH }, { "csh", no_argument, NULL, OPT_CSH }, { "options", required_argument, NULL, OPT_OPTIONS }, { "no-detach", no_argument, NULL, OPT_NO_DETACH }, { "homedir", required_argument, NULL, OPT_HOMEDIR }, { "uid-acl", required_argument, NULL, OPT_UID_ACL }, { "log-file", required_argument, NULL, OPT_LOG_FILE }, { "version", no_argument, NULL, OPT_VERSION }, { "help", no_argument, NULL, OPT_HELP }, { NULL, 0, NULL, 0 } }; int opt; enum { RUN_MODE_NONE, RUN_MODE_SERVER, RUN_MODE_MULTI_SERVER, RUN_MODE_DAEMON } run_mode = RUN_MODE_NONE; int env_is_csh = 0; int log_verbose = 0; int log_quiet = 0; int no_detach = 0; char *config_file = NULL; char *log_file = NULL; char *home_dir = NULL; int have_at_least_one_provider=0; FILE *fp_log = NULL; int i; CK_RV rv; global_t global; const char * CONFIG_SUFFIX = ".conf"; char *default_config_file = NULL; /* unused intentionally */ (void)log_quiet; memset(&global, 0, sizeof(global)); #if !defined(HAVE_W32_SYSTEM) s_parent_pid = getpid (); global.fd_accept_terminate[0] = -1; global.fd_accept_terminate[1] = -1; global.uid_acl = (uid_t)-1; #endif if ((default_config_file = (char *)malloc (strlen (PACKAGE)+strlen (CONFIG_SUFFIX)+1)) == NULL) { common_log (LOG_FATAL, "malloc failed"); } sprintf (default_config_file, "%s%s", PACKAGE, CONFIG_SUFFIX); common_set_log_stream (stderr); while ((opt = getopt_long (argc, argv, "vqsc", long_options, NULL)) != -1) { switch (opt) { case OPT_SERVER: run_mode = RUN_MODE_SERVER; break; case OPT_MUTLI_SERVER: run_mode = RUN_MODE_MULTI_SERVER; break; case OPT_DAEMON: run_mode = RUN_MODE_DAEMON; break; case OPT_VERBOSE: case 'v': log_verbose = 1; break; case OPT_QUIET: case 'q': log_quiet = 1; break; case OPT_SH: case 's': break; case OPT_CSH: case 'c': env_is_csh = 1; break; case OPT_OPTIONS: config_file = strdup (optarg); break; case OPT_NO_DETACH: no_detach = 1; break; case OPT_HOMEDIR: home_dir = strdup (optarg); break; #if !defined(HAVE_W32_SYSTEM) case OPT_UID_ACL: global.uid_acl = atoi(optarg); break; #endif case OPT_LOG_FILE: log_file = optarg; break; case OPT_VERSION: printf ( "%s %s\n" "\n" "Copyright (c) 2006-2007 Zeljko Vrba \n" "Copyright (c) 2006-2017 Alon Bar-Lev \n" "\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", PACKAGE, PACKAGE_VERSION ); exit (0); break; case OPT_HELP: usage(argv[0]); exit(0); break; default: fprintf(stderr, "Invalid usage\n"); exit(1); break; } } if (run_mode == RUN_MODE_NONE) { common_log (LOG_FATAL, "please use the option `--daemon' to run the program in the background"); } #if defined(HAVE_W32_SYSTEM) if (run_mode == RUN_MODE_DAEMON) { common_log (LOG_FATAL, "daemon mode is not supported"); } #endif if (home_dir == NULL) { home_dir = get_home_dir (); } if (config_file == NULL) { if ((config_file = (char *)malloc (strlen (home_dir) + strlen (default_config_file)+2)) == NULL) { common_log (LOG_FATAL, "malloc failed"); } sprintf (config_file, "%s%c%s", home_dir, CONFIG_PATH_SEPARATOR, default_config_file); } if ( !dconfig_read (config_file, &global.config) && !dconfig_read (CONFIG_SYSTEM_CONFIG, &global.config) ) { common_log (LOG_FATAL, "Cannot open configuration file"); } if (log_verbose) { global.config.verbose = 1; } #if !defined(HAVE_W32_SYSTEM) signal (SIGPIPE, SIG_IGN); { struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = on_signal; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGABRT, &action, NULL); sigaction(SIGHUP, &action, NULL); } #endif if (log_file == NULL) { log_file = global.config.log_file; } if (log_file != NULL) { if (strcmp (log_file, "stderr") != 0) { if ((fp_log = fopen (log_file, "a")) != NULL) { fchmod(fileno(fp_log), 0600); common_set_log_stream (fp_log); } } } if (global.config.debug) { common_log (LOG_DEBUG, "version: %s", PACKAGE_VERSION); dconfig_print (&global.config); common_log (LOG_DEBUG, "run_mode: %d", run_mode); common_log (LOG_DEBUG, "crypto: %s", #if defined(ENABLE_OPENSSL) "openssl" #elif defined(ENABLE_GNUTLS) "gnutls" #else "invalid" #endif ); } if (!gcry_check_version (GCRYPT_VERSION)) { common_log (LOG_FATAL, "Cannot initialize libcrypt"); } gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); #if !defined(HAVE_W32_SYSTEM) if (run_mode == RUN_MODE_DAEMON || run_mode == RUN_MODE_MULTI_SERVER) { server_socket_create_name (&global); } /* * fork before doing PKCS#11 stuff * some providers don't behave well */ if (run_mode == RUN_MODE_DAEMON) { pid_t pid; pid = fork (); if (pid == -1) { common_log (LOG_FATAL, "fork failed"); } if (pid != 0) { static const char *key = "SCDAEMON_INFO"; char env[1024]; snprintf (env, sizeof (env), "%s:%lu:1", global.socket_name, (unsigned long)pid); if (optind < argc) { setenv(key, env, 1); execvp (argv[optind], &(argv[optind])); kill (pid, SIGTERM); exit (1); } else { if (env_is_csh) { *strchr (env, '=') = ' '; printf ("setenv %s %s\n", key, env); } else { printf ("%s=%s; export %s\n", key, env, key); } exit (0); } } if (!no_detach) { int i; for (i=0;i<3;i++) { if (fileno (common_get_log_stream ()) != i) { close (i); } } if (setsid () == -1) { common_log (LOG_FATAL, "setsid failed"); } } if (chdir ("/") == -1) { common_log (LOG_FATAL, "chdir failed"); } if (optind < argc) { struct sigaction sa; memset (&sa, 0, sizeof (sa)); sigemptyset (&sa.sa_mask); #if defined(SA_INTERRUPT) sa.sa_flags |= SA_INTERRUPT; #endif sa.sa_handler = on_alarm; sigaction (SIGALRM, &sa, NULL); alarm (10); } } #endif /* HAVE_W32_SYSTEM */ assuan_set_assuan_log_prefix (PACKAGE); assuan_set_assuan_log_stream (common_get_log_stream ()); #if defined(USE_GNUTLS) if (gnutls_global_init () != GNUTLS_E_SUCCESS) { common_log (LOG_FATAL, "Cannot initialize gnutls"); } #endif if ((rv = pkcs11h_initialize ()) != CKR_OK) { common_log (LOG_FATAL, "Cannot initialize PKCS#11: %s", pkcs11h_getMessage (rv)); } pkcs11h_setLogLevel (global.config.verbose ? PKCS11H_LOG_DEBUG2 : PKCS11H_LOG_INFO); pkcs11h_setLogHook (pkcs11_log_hook, NULL); pkcs11h_setTokenPromptHook (pkcs11_token_prompt_hook, NULL); pkcs11h_setPINPromptHook (pkcs11_pin_prompt_hook, NULL); pkcs11h_setProtectedAuthentication (TRUE); for (i=0;i # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program 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 . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 24 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/misc/000077500000000000000000000000001316227662100204365ustar00rootroot00000000000000gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/misc/init-token-gnutls.sh000077500000000000000000000030461316227662100243730ustar00rootroot00000000000000#!/bin/sh PIN=user #SOPIN=sopin TOKEN=test1 OBJECT=key ID=0 KEY_SIZE=2048 TEMPLATE=my.template for PROVIDER in /usr/lib64/pkcs11/libsofthsm2.so /usr/lib64/softhsm/libsofthsm2.so; do [ -f "${PROVIDER}" ] && break done for P11ENGINE in /usr/lib64/engines-1.1/pkcs11.so /usr/lib64/engines/pkcs11.so; do [ -f "${P11ENGINE}" ] && break done die() { local m="$1" echo "FATAL: ${m}" >&2 exit 1 } MYTMP= trap cleanup 0 cleanup() { [ -n "${MYTMP}" ] && rm -fr "${MYTMP}" } MYTMP="$(mktemp -d)" #softhsm2-util --init-token --label "${TOKEN}" --free --so-pin "${SOPIN}" --pin "${PIN}" export GNUTLS_PIN="${PIN}" for i in 1 2 3; do myobject="${OBJECT}${i}" myid="${ID}${i}" mytemplate="${MYTMP}/${i}.template" cat > "${mytemplate}" << __EOF__ cn = "Dummy ${myid}" serial = 00${myid} expiration_days = 3600 __EOF__ p11tool \ --provider="${PROVIDER}" \ --login \ --generate-rsa \ --bits="${KEY_SIZE}" \ --id="${myid}" --label="${myobject}" \ "pkcs11:token=${TOKEN}" \ || die "Cannot generate key" certtool \ --provider="${PROVIDER}" \ --generate-self-signed \ --load-privkey="pkcs11:token=${TOKEN};object=${myobject};type=private" \ --load-pubkey="pkcs11:token=${TOKEN};object=${myobject};type=public" \ --template="${mytemplate}" \ --outfile="${MYTMP}/cert.pem" \ || die "Cannot enroll certificate" p11tool \ --provider="${PROVIDER}" \ --login \ --write \ --id="${myid}" --label="${myobject}" \ --no-mark-private \ --load-certificate="${MYTMP}/cert.pem" \ "pkcs11:token=${TOKEN}" \ || die "Cannot store certificate" done gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/misc/init-token-openssl.sh000077500000000000000000000035661316227662100245510ustar00rootroot00000000000000#!/bin/sh PIN=user #SOPIN=sopin TOKEN=test1 OBJECT=key ID=0 KEY_SIZE=2048 SUBJECT="/CN=Test" for PROVIDER in /usr/lib64/pkcs11/libsofthsm2.so /usr/lib64/softhsm/libsofthsm2.so; do [ -f "${PROVIDER}" ] && break done for P11ENGINE in /usr/lib64/engines-1.1/pkcs11.so /usr/lib64/engines/pkcs11.so; do [ -f "${P11ENGINE}" ] && break done die() { local m="$1" echo "FATAL: ${m}" >&2 exit 1 } MYTMP= trap cleanup 0 cleanup() { [ -n "${MYTMP}" ] && rm -fr "${MYTMP}" } MYTMP="$(mktemp -d)" #softhsm2-util --init-token --label "${TOKEN}" --free --so-pin "${SOPIN}" --pin "${PIN}" for i in 1 2 3; do myobject="${OBJECT}${i}" myid="${ID}${i}" mysubject="${SUBJECT} ${i}" pkcs11-tool \ --module "${PROVIDER}" \ --token-label "${TOKEN}" \ --login \ --pin "${PIN}" \ --id "${myid}" --label "${myobject}" \ --key-type rsa:${KEY_SIZE} \ --keypairgen || \ die "Cannot generate key" # twice req as it succeeds at second.... openssl << __EOF__ || die "Cannot enroll certificate" engine -t dynamic \ -pre SO_PATH:${P11ENGINE} \ -pre ID:pkcs11 \ -pre LIST_ADD:1 \ -pre LOAD \ -pre MODULE_PATH:${PROVIDER} req \ -engine pkcs11 \ -new \ -key "pkcs11:token=${TOKEN};id=%0$i;type=private;pin-value=${PIN}" \ -keyform engine -out "${MYTMP}/req.pem" -text -x509 -subj "${mysubject}" req \ -engine pkcs11 \ -new \ -key "pkcs11:token=${TOKEN};id=%0$i;type=private;pin-value=${PIN}" \ -keyform engine -out "${MYTMP}/req.pem" -text -x509 -subj "${mysubject}" x509 \ -engine pkcs11 \ -signkey "pkcs11:token=${TOKEN};id=%0$i;type=private;pin-value=${PIN}" \ -keyform engine -in "${MYTMP}/req.pem" -out "${MYTMP}/cert.der" -outform DER __EOF__ pkcs11-tool \ --module "${PROVIDER}" \ --token-label "${TOKEN}" \ --login \ --pin "${PIN}" \ --id "${myid}" --label "${myobject}" \ --type cert \ --write-object \ "${MYTMP}/cert.der" || \ die "Cannot store certificate" done gnupg-pkcs11-scd-gnupg-pkcs11-scd-0.9.1/misc/pinentry-file000077500000000000000000000035541316227662100231600ustar00rootroot00000000000000#!/usr/bin/python import argparse import sys def parse_args(): class MyArgumentParser(argparse.ArgumentParser): def error(self, message): raise RuntimeError(message) def exit(self, status=0, message=None): raise RuntimeError(message) parser = MyArgumentParser( prog='pinentry-file', description=( 'File based pinentry' ), ) parser.add_argument( '--file', required=True, ) return parser.parse_args() def write(s): sys.stdout.write(s) sys.stdout.flush() def main(): try: args = parse_args() write( 'OK Pleased to meet you\n' ) while True: # HACK-BEGIN # python2 line = sys.stdin.readline() if not line: break # HACK-END line = line.strip() s = line.split(' ', 2) command = s[0] if command in ( 'SETDESC', 'SETPROMPT', 'SETTITLE', 'SETOK', 'SETCANCEL', 'SETERROR', 'SETQUALITYBAR', 'SETQUALITYBAR_TT', 'CONFIRM', 'MESSAGE', 'OPTION', ): write('OK\n') elif command == 'GETPIN': with open(args.file) as f: write('D %s\n' % f.readline().rstrip('\r\n')) write('OK\n') else: write( 'ERR 536871187 Unknown ' + 'IPC command \n' ) except Exception as e: try: write( 'ERR 536871187 %s\n' % e ) except Exception: pass if __name__ == '__main__': main()