From 435d1f5256782583b8b427cd7b07f71dca58b6bd Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Tue, 9 Feb 2021 16:00:32 +0900 Subject: [PATCH] Imported Upstream version 2.2.5 --- AUTHORS | 10 +- NEWS | 47 ++++++++++ README | 4 +- agent/command-ssh.c | 3 + agent/command.c | 46 ++------- autogen.rc | 2 + autogen.sh | 7 +- build-aux/speedo.mk | 27 +++++- common/argparse.c | 2 +- common/asshelp.h | 7 ++ common/asshelp2.c | 63 +++++++++++++ common/homedir.c | 2 +- common/logging.h | 26 ++++++ common/w32info-rc.h.in | 2 +- configure.ac | 26 ++++-- dirmngr/server.c | 28 ++---- doc/examples/vsnfd.prf | 1 + doc/gpg.texi | 32 +++++-- doc/gpgsm.texi | 6 +- doc/wks.texi | 27 +++--- g10/card-util.c | 2 + g10/getkey.c | 15 ++- g10/keydb.c | 5 +- g10/keyedit.c | 4 +- g10/keygen.c | 67 ++++++++++---- g10/keyring.c | 4 +- g10/mainproc.c | 10 +- g13/server.c | 21 +---- g13/sh-cmd.c | 28 +----- kbx/kbxutil.c | 1 - kbx/keybox-defs.h | 69 +------------- kbx/keybox-search.c | 16 ++-- kbx/keybox-util.c | 53 ----------- kbx/keybox.h | 4 - scd/app-openpgp.c | 128 +++++++++++++++++++------ scd/ccid-driver.c | 17 +++- scd/command.c | 4 +- scd/scdaemon.c | 39 +++++++- sm/export.c | 3 +- sm/gpgsm.c | 2 - sm/server.c | 21 +---- tests/asschk.c | 2 +- tools/gpg-wks-client.c | 167 ++++++++------------------------- tools/gpg-wks-server.c | 246 +++++++++++++++++++++++++++++++++++++++++++------ tools/gpg-wks.h | 6 +- tools/wks-util.c | 158 +++++++++++++++++++++++++++++-- 46 files changed, 933 insertions(+), 527 deletions(-) diff --git a/AUTHORS b/AUTHORS index d27dfb6..473b94c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,9 +15,9 @@ copyrightable year that would otherwise be listed individually. List of Copyright holders ========================= - Copyright (C) 1997-2017 Werner Koch - Copyright (C) 1994-2017 Free Software Foundation, Inc. - Copyright (C) 2003-2013,2015-2017 g10 Code GmbH + Copyright (C) 1997-2018 Werner Koch + Copyright (C) 1994-2018 Free Software Foundation, Inc. + Copyright (C) 2003-2013,2015-2018 g10 Code GmbH Copyright (C) 2002 Klarälvdalens Datakonsult AB Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper Copyright (C) 1994 X Consortium @@ -91,8 +91,8 @@ domain. ========= - Copyright 1998-2017 Free Software Foundation, Inc. - Copyright 1997-2017 Werner Koch + Copyright 1998-2018 Free Software Foundation, Inc. + Copyright 1997-2018 Werner Koch This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/NEWS b/NEWS index 59b2a45..5c8b499 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,48 @@ +Noteworthy changes in version 2.2.5 (2018-02-22) +------------------------------------------------ + + * gpg: Allow the use of the "cv25519" and "ed25519" short names in + addition to the canonical curve names in --batch --gen-key. + + * gpg: Make sure to print all secret keys with option --list-only + and --decrypt. [#3718] + + * gpg: Fix the use of future-default with --quick-add-key for + signing keys. [#3747] + + * gpg: Select a secret key by checking availability under gpg-agent. + [#1967] + + * gpg: Fix reversed prompt texts for --only-sign-text-ids. [#3787] + + * gpg,gpgsm: Fix detection of bogus keybox blobs on 32 bit systems. + [#3770] + + * gpgsm: Fix regression since 2.1 in --export-secret-key-raw which + got $d mod (q-1)$ wrong. Note that most tools automatically fixup + that parameter anyway. + + * ssh: Fix a regression in getting the client'd PID on *BSD and + macOS. + + * scd: Support the KDF Data Object of the OpenPGP card 3.3. [#3152] + + * scd: Fix a regression in the internal CCID driver for certain card + readers. [#3508] + + * scd: Fix a problem on NetBSD killing scdaemon on gpg-agent + shutdown. [#3778] + + * dirmngr: Improve returned error description on failure of DNS + resolving. [#3756] + + * wks: Implement command --install-key for gpg-wks-server. + + * Add option STATIC=1 to the Speedo build system to allow a build + with statically linked versions of the core GnuPG libraries. Also + use --enable-wks-tools by default by Speedo builds for Unix. + + Noteworthy changes in version 2.2.4 (2017-12-20) ------------------------------------------------ @@ -28,6 +73,8 @@ Noteworthy changes in version 2.2.4 (2017-12-20) * New configure option --enable-run-gnupg-user-socket to first try a socket directory which is not removed by systemd at session end. + See-also: gnupg-announce/2017q4/000419.html + Noteworthy changes in version 2.2.3 (2017-11-20) ------------------------------------------------ diff --git a/README b/README index 23f705a..d46efd9 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ ========================= Version 2.2 - Copyright 1997-2017 Werner Koch - Copyright 1998-2017 Free Software Foundation, Inc. + Copyright 1997-2018 Werner Koch + Copyright 1998-2018 Free Software Foundation, Inc. * INTRODUCTION diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 9d45a18..e0b7238 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -44,6 +44,9 @@ #include #include #endif /*!HAVE_W32_SYSTEM*/ +#ifdef HAVE_SYS_UCRED_H +#include +#endif #ifdef HAVE_UCRED_H #include #endif diff --git a/agent/command.c b/agent/command.c index 0916f88..f9bc6ca 100644 --- a/agent/command.c +++ b/agent/command.c @@ -293,50 +293,19 @@ parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf) /* Write an Assuan status line. KEYWORD is the first item on the - status line. The following arguments are all separated by a space - in the output. The last argument must be a NULL. Linefeeds and - carriage returns characters (which are not allowed in an Assuan - status line) are silently quoted in C-style. */ + * status line. The following arguments are all separated by a space + * in the output. The last argument must be a NULL. Linefeeds and + * carriage returns characters (which are not allowed in an Assuan + * status line) are silently quoted in C-style. */ gpg_error_t agent_write_status (ctrl_t ctrl, const char *keyword, ...) { - gpg_error_t err = 0; + gpg_error_t err; va_list arg_ptr; - const char *text; assuan_context_t ctx = ctrl->server_local->assuan_ctx; - char buf[950], *p; - size_t n; va_start (arg_ptr, keyword); - - p = buf; - n = 0; - while ( (text = va_arg (arg_ptr, const char *)) ) - { - if (n) - { - *p++ = ' '; - n++; - } - for ( ; *text && n < DIM (buf)-3; n++, text++) - { - if (*text == '\n') - { - *p++ = '\\'; - *p++ = 'n'; - } - else if (*text == '\r') - { - *p++ = '\\'; - *p++ = 'r'; - } - else - *p++ = *text; - } - } - *p = 0; - err = assuan_write_status (ctx, keyword, buf); - + err = vprint_assuan_status_strings (ctx, keyword, arg_ptr); va_end (arg_ptr); return err; } @@ -1062,7 +1031,8 @@ cmd_readkey (assuan_context_t ctx, char *line) rc = gpg_error_from_syserror (); else { - gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen); + pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, + pkbuf, pkbuflen); rc = assuan_send_data (ctx, pkbuf, pkbuflen); } } diff --git a/autogen.rc b/autogen.rc index 6835929..3ea03e0 100644 --- a/autogen.rc +++ b/autogen.rc @@ -1,5 +1,7 @@ # autogen.sh configuration for GnuPG -*- sh -*- +display_name=GnuPG + #version_parts=3 case "$myhost:$myhostsub" in diff --git a/autogen.sh b/autogen.sh index e5ba5bf..b238550 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,6 +1,6 @@ #! /bin/sh # autogen.sh -# Copyright (C) 2003, 2014, 2017 g10 Code GmbH +# Copyright (C) 2003, 2014, 2017, 2018 g10 Code GmbH # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without @@ -15,7 +15,7 @@ # configure it for the respective package. It is maintained as part of # GnuPG and source copied by other packages. # -# Version: 2017-01-17 +# Version: 2018-02-21 configure_ac="configure.ac" @@ -467,6 +467,9 @@ EOF EOF $CP build-aux/git-hooks/commit-msg .git/hooks/commit-msg chmod +x .git/hooks/commit-msg + if [ x"${display_name}" != x ]; then + git config format.subjectPrefix "PATCH ${display_name}" + fi fi fi diff --git a/build-aux/speedo.mk b/build-aux/speedo.mk index 2b3b72b..320d440 100644 --- a/build-aux/speedo.mk +++ b/build-aux/speedo.mk @@ -63,6 +63,7 @@ help: @echo 'You may append INSTALL_PREFIX= for native builds.' @echo 'Prepend TARGET with "git-" to build from GIT repos.' @echo 'Prepend TARGET with "this-" to build from the source tarball.' + @echo 'Use STATIC=1 to build with statically linked libraries.' @echo 'Use SELFCHECK=0 for a non-released version.' @echo 'Use CUSTOM_SWDB=1 for an already downloaded swdb.lst.' @@ -140,6 +141,9 @@ UPD_SWDB=0 # Set to 0 to skip the GnuPG version self-check SELFCHECK=1 +# Set to 1 to build with statically linked libraries. +STATIC=0 + # Set to the location of the directory with tarballs of # external packages. TARBALLS=$(shell pwd)/../tarballs @@ -208,8 +212,10 @@ speedo_spkgs += \ endif endif +ifeq ($(STATIC),0) speedo_spkgs += \ gpgme +endif ifeq ($(TARGETOS),w32) ifeq ($(WITH_GUI),1) @@ -461,6 +467,8 @@ speedo_pkg_gtk__tar = $(pkg2rep)/gtk+-$(gtk__ver).tar.xz # Package build options # +speedo_pkg_npth_configure = --enable-static + speedo_pkg_libgpg_error_configure = --enable-static speedo_pkg_w64_libgpg_error_configure = --enable-static @@ -471,15 +479,30 @@ speedo_pkg_libgcrypt_configure = --disable-static speedo_pkg_libksba_configure = --disable-static +speedo_pkg_ntbtls_configure = --enable-static + + +ifeq ($(STATIC),1) +speedo_pkg_npth_configure += --disable-shared + +speedo_pkg_libgpg_error_configure += --disable-shared + +speedo_pkg_libassuan_configure += --disable-shared + +speedo_pkg_libgcrypt_configure += --disable-shared + +speedo_pkg_libksba_configure += --disable-shared +endif + # For now we build ntbtls only static -speedo_pkg_ntbtls_configure = --enable-static --disable-shared +speedo_pkg_ntbtls_configure = --disable-shared ifeq ($(TARGETOS),w32) speedo_pkg_gnupg_configure = \ --disable-g13 --enable-ntbtls \ --enable-build-timestamp else -speedo_pkg_gnupg_configure = --disable-g13 +speedo_pkg_gnupg_configure = --disable-g13 --enable-wks-tools endif speedo_pkg_gnupg_extracflags = -g diff --git a/common/argparse.c b/common/argparse.c index f5e4ceb..90d0ff7 100644 --- a/common/argparse.c +++ b/common/argparse.c @@ -71,7 +71,7 @@ #else /* Used by GnuPG */ # define ARGPARSE_GPL_VERSION 3 -# define ARGPARSE_CRIGHT_STR "Copyright (C) 2017 Free Software Foundation, Inc." +# define ARGPARSE_CRIGHT_STR "Copyright (C) 2018 Free Software Foundation, Inc." #endif /*GNUPG_MAJOR_VERSION*/ diff --git a/common/asshelp.h b/common/asshelp.h index f169d87..bf1bd17 100644 --- a/common/asshelp.h +++ b/common/asshelp.h @@ -93,5 +93,12 @@ gpg_error_t vprint_assuan_status (assuan_context_t ctx, const char *format, va_list arg_ptr) GPGRT_ATTR_PRINTF(3,0); +gpg_error_t vprint_assuan_status_strings (assuan_context_t ctx, + const char *keyword, + va_list arg_ptr); +gpg_error_t print_assuan_status_strings (assuan_context_t ctx, + const char *keyword, + ...) GPGRT_ATTR_SENTINEL(1); + #endif /*GNUPG_COMMON_ASSHELP_H*/ diff --git a/common/asshelp2.c b/common/asshelp2.c index f85c1e6..0a7c454 100644 --- a/common/asshelp2.c +++ b/common/asshelp2.c @@ -71,3 +71,66 @@ print_assuan_status (assuan_context_t ctx, va_end (arg_ptr); return err; } + + +/* Helper function to print a list of strings as an assuan status + * line. KEYWORD is the first item on the status line. ARG_PTR is a + * list of strings which are all separated by a space in the output. + * The last argument must be a NULL. Linefeeds and carriage returns + * characters (which are not allowed in an Assuan status line) are + * silently quoted in C-style. */ +gpg_error_t +vprint_assuan_status_strings (assuan_context_t ctx, + const char *keyword, va_list arg_ptr) +{ + gpg_error_t err = 0; + const char *text; + char buf[950], *p; + size_t n; + + p = buf; + n = 0; + while ((text = va_arg (arg_ptr, const char *)) && n < DIM (buf)-3 ) + { + if (n) + { + *p++ = ' '; + n++; + } + for ( ; *text && n < DIM (buf)-3; n++, text++) + { + if (*text == '\n') + { + *p++ = '\\'; + *p++ = 'n'; + n++; + } + else if (*text == '\r') + { + *p++ = '\\'; + *p++ = 'r'; + n++; + } + else + *p++ = *text; + } + } + *p = 0; + err = assuan_write_status (ctx, keyword, buf); + + return err; +} + + +/* See vprint_assuan_status_strings. */ +gpg_error_t +print_assuan_status_strings (assuan_context_t ctx, const char *keyword, ...) +{ + va_list arg_ptr; + gpg_error_t err; + + va_start (arg_ptr, keyword); + err = vprint_assuan_status_strings (ctx, keyword, arg_ptr); + va_end (arg_ptr); + return err; +} diff --git a/common/homedir.c b/common/homedir.c index a598900..65cf50f 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -554,7 +554,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) }; int i; struct stat sb; - char prefix[13 + 1 + 20 + 6 + 1]; + char prefix[19 + 1 + 20 + 6 + 1]; const char *s; char *name = NULL; diff --git a/common/logging.h b/common/logging.h index e1bf56b..2225100 100644 --- a/common/logging.h +++ b/common/logging.h @@ -112,4 +112,30 @@ void log_printhex (const char *text, const void *buffer, size_t length); void log_clock (const char *string); +/* Some handy assertion macros which don't abort. */ + +#define return_if_fail(expr) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return; \ + } } while (0) +#define return_null_if_fail(expr) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return NULL; \ + } } while (0) +#define return_val_if_fail(expr,val) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return (val); \ + } } while (0) +#define never_reached() do { \ + log_debug ("%s:%d: oops - should never get here\n", \ + __FILE__, __LINE__ ); \ + } while (0) + + #endif /*GNUPG_COMMON_LOGGING_H*/ diff --git a/common/w32info-rc.h.in b/common/w32info-rc.h.in index 4e46b97..2ff6863 100644 --- a/common/w32info-rc.h.in +++ b/common/w32info-rc.h.in @@ -29,4 +29,4 @@ built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0" #define W32INFO_PRODUCTVERSION "@VERSION@\0" #define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \ -2017 Free Software Foundation, Inc.\0" +2018 Free Software Foundation, Inc.\0" diff --git a/configure.ac b/configure.ac index 382ef1d..e249361 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac - for GnuPG 2.1 -# Copyright (C) 1998-2017 Free Software Foundation, Inc. -# Copyright (C) 1998-2017 Werner Koch +# Copyright (C) 1998-2018 Free Software Foundation, Inc. +# Copyright (C) 1998-2018 Werner Koch # # This file is part of GnuPG. # @@ -28,7 +28,7 @@ min_automake_version="1.14" m4_define([mym4_package],[gnupg]) m4_define([mym4_major], [2]) m4_define([mym4_minor], [2]) -m4_define([mym4_micro], [4]) +m4_define([mym4_micro], [5]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release @@ -59,7 +59,7 @@ NEED_LIBGCRYPT_API=1 NEED_LIBGCRYPT_VERSION=1.7.0 NEED_LIBASSUAN_API=2 -NEED_LIBASSUAN_VERSION=2.4.3 +NEED_LIBASSUAN_VERSION=2.5.0 NEED_KSBA_API=1 NEED_KSBA_VERSION=1.3.4 @@ -639,6 +639,7 @@ have_android_system=no use_simple_gettext=no use_ldapwrapper=yes mmap_needed=yes +require_pipe_to_unblock_pselect=no case "${host}" in *-mingw32*) # special stuff for Windoze NT @@ -715,10 +716,20 @@ case "${host}" in AC_DEFINE(_DARWIN_C_SOURCE, 900000L, Expose all libc features (__DARWIN_C_FULL).) ;; + *-*-netbsd*) + require_pipe_to_unblock_pselect=yes + ;; *) - ;; + ;; esac +if test "$require_pipe_to_unblock_pselect" = yes; then + AC_DEFINE(HAVE_PSELECT_NO_EINTR, 1, + [Defined if we run on systems like NetBSD, where + pselect cannot be unblocked by signal from a thread + within the same process. We use pipe in this case, instead.]) +fi + if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems @@ -820,7 +831,8 @@ if test x"$LIBUSB_NAME" != x ; then have_libusb=yes ]) AC_MSG_CHECKING([libusb include dir]) usb_incdir_found="no" - for _incdir in "" "/usr/include/libusb-1.0" "/usr/local/include/libusb-1.0"; do + for _incdir in "" "/usr/include/libusb-1.0" \ + "/usr/local/include/libusb-1.0" "/usr/pkg/include/libusb-1.0"; do _libusb_save_cppflags=$CPPFLAGS if test -n "${_incdir}"; then CPPFLAGS="-I${_incdir} ${CPPFLAGS}" @@ -1278,7 +1290,7 @@ AC_HEADER_STDC AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \ pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h \ stdint.h signal.h util.h libutil.h termios.h \ - ucred.h sys/sysmacros.h sys/mkdev.h]) + ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h]) AC_HEADER_TIME diff --git a/dirmngr/server.c b/dirmngr/server.c index 3d0768b..60d9802 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -2134,8 +2134,13 @@ cmd_keyserver (assuan_context_t ctx, char *line) if (resolve_flag) { err = ensure_keyserver (ctrl); - if (!err) - err = ks_action_resolve (ctrl, ctrl->server_local->keyservers); + if (err) + { + assuan_set_error (ctx, err, + "Bad keyserver configuration in dirmngr.conf"); + goto leave; + } + err = ks_action_resolve (ctrl, ctrl->server_local->keyservers); if (err) goto leave; } @@ -2829,30 +2834,13 @@ dirmngr_status (ctrl_t ctrl, const char *keyword, ...) { gpg_error_t err = 0; va_list arg_ptr; - const char *text; assuan_context_t ctx; va_start (arg_ptr, keyword); if (ctrl->server_local && (ctx = ctrl->server_local->assuan_ctx)) { - char buf[950], *p; - size_t n; - - p = buf; - n = 0; - while ( (text = va_arg (arg_ptr, const char *)) ) - { - if (n) - { - *p++ = ' '; - n++; - } - for ( ; *text && n < DIM (buf)-2; n++) - *p++ = *text++; - } - *p = 0; - err = assuan_write_status (ctx, keyword, buf); + err = vprint_assuan_status_strings (ctx, keyword, arg_ptr); } va_end (arg_ptr); diff --git a/doc/examples/vsnfd.prf b/doc/examples/vsnfd.prf index e8732de..1dc21e0 100644 --- a/doc/examples/vsnfd.prf +++ b/doc/examples/vsnfd.prf @@ -6,6 +6,7 @@ default-new-key-algo rsa3072/cert,sign+rsa3072/encr [gpgsm] enable-crl-checks +compliance de-vs [gpg-agent] enable-extended-key-format diff --git a/doc/gpg.texi b/doc/gpg.texi index 35bb9a8..ddebc69 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -622,7 +622,9 @@ This section explains the main commands for key management. @table @gnupgtabopt @item --quick-generate-key @var{user-id} [@var{algo} [@var{usage} [@var{expire}]]] +@itemx --quick-gen-key @opindex quick-generate-key +@opindex quick-gen-key This is a simple command to generate a standard key with one user id. In contrast to @option{--generate-key} the key is generated directly without the need to answer a bunch of prompts. Unless the option @@ -3082,8 +3084,9 @@ will be read from file descriptor @var{n}. If you use 0 for @var{n}, the passphrase will be read from STDIN. This can only be used if only one passphrase is supplied. -Note that this passphrase is only used if the option @option{--batch} -has also been given. This is different from GnuPG version 1.x. +Note that since Version 2.0 this passphrase is only used if the +option @option{--batch} has also been given. Since Version 2.1 +the @option{--pinentry-mode} also needs to be set to @code{loopback}. @item --passphrase-file @var{file} @opindex passphrase-file @@ -3092,8 +3095,10 @@ be read from file @var{file}. This can only be used if only one passphrase is supplied. Obviously, a passphrase stored in a file is of questionable security if other users can read this file. Don't use this option if you can avoid it. -Note that this passphrase is only used if the option @option{--batch} -has also been given. This is different from GnuPG version 1.x. + +Note that since Version 2.0 this passphrase is only used if the +option @option{--batch} has also been given. Since Version 2.1 +the @option{--pinentry-mode} also needs to be set to @code{loopback}. @item --passphrase @var{string} @opindex passphrase @@ -3101,8 +3106,10 @@ Use @var{string} as the passphrase. This can only be used if only one passphrase is supplied. Obviously, this is of very questionable security on a multi-user system. Don't use this option if you can avoid it. -Note that this passphrase is only used if the option @option{--batch} -has also been given. This is different from GnuPG version 1.x. + +Note that since Version 2.0 this passphrase is only used if the +option @option{--batch} has also been given. Since Version 2.1 +the @option{--pinentry-mode} also needs to be set to @code{loopback}. @item --pinentry-mode @var{mode} @opindex pinentry-mode @@ -3774,6 +3781,19 @@ If you are going to verify detached signatures, make sure that the program knows about it; either give both filenames on the command line or use @samp{-} to specify STDIN. +For scripted or other unattended use of @command{gpg} make sure to use +the machine-parseable interface and not the default interface which is +intended for direct use by humans. The machine-parseable interface +provides a stable and well documented API independent of the locale or +future changes of @command{gpg}. To enable this interface use the +options @option{--with-colons} and @option{--status-fd}. For certain +operations the option @option{--command-fd} may come handy too. See +this man page and the file @file{DETAILS} for the specification of the +interface. Note that the GnuPG ``info'' pages as well as the PDF +version of the GnuPG manual features a chapter on unattended use of +GnuPG. As an alternative the library @command{GPGME} can be used as a +high-level abstraction on top of that interface. + @mansect interoperability @chapheading INTEROPERABILITY WITH OTHER OPENPGP PROGRAMS diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 5d79ce5..cd2a741 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -257,10 +257,10 @@ fingerprints or keygrips. @item --export-secret-key-p12 @var{key-id} @opindex export-secret-key-p12 Export the private key and the certificate identified by @var{key-id} in -a PKCS#12 format. When used with the @code{--armor} option a few +using the PKCS#12 format. When used with the @code{--armor} option a few informational lines are prepended to the output. Note, that the PKCS#12 -format is not very secure and this command is only provided if there is -no other way to exchange the private key. (@xref{option --p12-charset}.) +format is not very secure and proper transport security should be used +to convey the exported key. (@xref{option --p12-charset}.) @item --export-secret-key-p8 @var{key-id} @itemx --export-secret-key-raw @var{key-id} diff --git a/doc/wks.texi b/doc/wks.texi index 7f7d515..6d62282 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -181,6 +181,7 @@ Display a brief help page and exit. .RI [ options ] .B \-\-install-key .I file +.I user-id .br .B gpg-wks-server .RI [ options ] @@ -221,14 +222,19 @@ the process returns failure; to suppress the diagnostic, use option @option{-q}. More than one user-id can be given; see also option @option{with-file}. +The command @option{--install-key} manually installs a key into the +WKD. The arguments are a file with the keyblock and the user-id to +install. If the first argument resembles a fingerprint the key is +taken from the current keyring; to force the use of a file, prefix the +first argument with "./". + The command @option{--remove-key} uninstalls a key from the WKD. The -process return success in this case; to also print a diagnostic, use -option @option{-v}. If the key is not installed a diagnostics is +process returns success in this case; to also print a diagnostic, use +option @option{-v}. If the key is not installed a diagnostic is printed and the process returns failure; to suppress the diagnostic, use option @option{-q}. -The commands @option{--install-key} and @option{--revoke-key} are not -yet functional. +The command @option{--revoke-key} is not yet functional. @mansect options @@ -326,7 +332,7 @@ the submission address: @example $ gpg --batch --passphrase '' --quick-gen-key key-submission@@example.net - $ gpg --with-wkd-hash -K key-submission@@example.net + $ gpg -K key-submission@@example.net @end example The output of the last command looks similar to this: @@ -335,21 +341,16 @@ The output of the last command looks similar to this: sec rsa2048 2016-08-30 [SC] C0FCF8642D830C53246211400346653590B3795B uid [ultimate] key-submission@@example.net - bxzcxpxk8h87z1k7bzk86xn5aj47intu@@example.net ssb rsa2048 2016-08-30 [E] @end example -Take the hash of the string "key-submission", which is -"bxzcxpxk8h87z1k7bzk86xn5aj47intu" and manually publish that key: +Take the fingerprint from that output and manually publish the key: @example - $ gpg --export-options export-minimal --export \ - > -o /var/lib/gnupg/wks/example.net/hu/bxzcxpxk8h87z1k7bzk86xn5aj47intu \ - > key-submission@@example.new + $ gpg-wks-server --install-key C0FCF8642D830C53246211400346653590B3795B \ + > key-submission@@example.net @end example -Make sure that the created file is world readable. - Finally that submission address needs to be redirected to a script running @command{gpg-wks-server}. The @command{procmail} command can be used for this: Redirect the submission address to the user "webkey" diff --git a/g10/card-util.c b/g10/card-util.c index a396b7d..759dde8 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -212,9 +212,11 @@ get_manufacturer (unsigned int no) case 0x000A: return "Dangerous Things"; case 0x002A: return "Magrathea"; + case 0x0042: return "GnuPG e.V."; case 0x1337: return "Warsaw Hackerspace"; case 0x2342: return "warpzone"; /* hackerspace Muenster. */ + case 0xBD0E: return "Paranoidlabs"; case 0xF517: return "FSIJ"; /* 0x0000 and 0xFFFF are defined as test cards per spec, diff --git a/g10/getkey.c b/g10/getkey.c index e31e023..dabd052 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -144,7 +144,7 @@ static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret, kbnode_t *ret_keyblock, kbnode_t *ret_found_key); static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, - unsigned int *r_flags); + int want_secret, unsigned int *r_flags); static void print_status_key_considered (kbnode_t keyblock, unsigned int flags); @@ -1743,7 +1743,7 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. FIXME: Check whether this still holds. */ merge_selfsigs (ctrl, keyblock); - found_key = finish_lookup (keyblock, pk->req_usage, 0, &infoflags); + found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) pk_from_block (pk, keyblock, found_key); @@ -3494,7 +3494,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) */ static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, - unsigned int *r_flags) + int want_secret, unsigned int *r_flags) { kbnode_t k; @@ -3636,6 +3636,13 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, continue; } + if (want_secret && agent_probe_secret_key (NULL, pk)) + { + if (DBG_LOOKUP) + log_debug ("\tno secret key\n"); + continue; + } + if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure @@ -3823,7 +3830,7 @@ lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret, * merge_selfsigs. */ merge_selfsigs (ctrl, keyblock); found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact, - &infoflags); + want_secret, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) { diff --git a/g10/keydb.c b/g10/keydb.c index 58a14a8..03fadfd 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1269,10 +1269,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, break; /* Allowed per RFC. */ default: - /* Note that can't allow ring trust packets here and some of - the other GPG specific packets don't make sense either. */ - log_error ("skipped packet of type %d in keybox\n", - (int)pkt->pkttype); + log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; diff --git a/g10/keyedit.c b/g10/keyedit.c index 4acb2de..17cf7d6 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1664,11 +1664,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, if (opt.only_sign_text_ids) result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", - _("Really sign all user IDs? (y/N) ")); + _("Really sign all text user IDs? (y/N) ")); else result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", - _("Really sign all text user IDs? (y/N) ")); + _("Really sign all user IDs? (y/N) ")); if (! result) { diff --git a/g10/keygen.c b/g10/keygen.c index b42afa8..8de6538 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1581,6 +1581,13 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); + /* Map the displayed short forms of some curves to their canonical + * names. */ + if (!ascii_strcasecmp (curve, "cv25519")) + curve = "Curve25519"; + else if (!ascii_strcasecmp (curve, "ed25519")) + curve = "Ed25519"; + /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ if (algo == PUBKEY_ALGO_EDDSA) @@ -2888,9 +2895,11 @@ generate_user_id (KBNODE keyblock, const char *uidstr) * success is returned. On error an error code is returned. Note * that STRING may be modified by this function. NULL may be passed * for any parameter. FOR_SUBKEY shall be true if this is used as a + * subkey. If CLEAR_CERT is set a default CERT usage will be cleared; + * this is useful if for example the default algorithm is used for a * subkey. */ static gpg_error_t -parse_key_parameter_part (char *string, int for_subkey, +parse_key_parameter_part (char *string, int for_subkey, int clear_cert, int *r_algo, unsigned int *r_size, unsigned int *r_keyuse, char const **r_curve) @@ -3041,6 +3050,10 @@ parse_key_parameter_part (char *string, int for_subkey, if (!for_subkey) keyuse |= PUBKEY_USAGE_CERT; + /* But if requested remove th cert usage. */ + if (clear_cert) + keyuse &= ~PUBKEY_USAGE_CERT; + /* Check that usage is actually possible. */ if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) && !pubkey_get_nsig (algo)) @@ -3112,14 +3125,16 @@ parse_key_parameter_part (char *string, int for_subkey, * -1 := Both parts * 0 := Only the part of the primary key * 1 := If there is one part parse that one, if there are - * two parts parse the second part. Always return - * in the args for the primary key (R_ALGO,....). + * two parts parse the part which best matches the + * SUGGESTED_USE or in case that can't be evaluated the second part. + * Always return using the args for the primary key (R_ALGO,....). * */ gpg_error_t parse_key_parameter_string (const char *string, int part, + unsigned int suggested_use, int *r_algo, unsigned int *r_size, - unsigned *r_keyuse, + unsigned int *r_keyuse, char const **r_curve, int *r_subalgo, unsigned int *r_subsize, unsigned *r_subkeyuse, @@ -3146,9 +3161,10 @@ parse_key_parameter_string (const char *string, int part, *r_subcurve = NULL; if (!string || !*string - || !strcmp (string, "default") || !strcmp (string, "-")) + || !ascii_strcasecmp (string, "default") || !strcmp (string, "-")) string = get_default_pubkey_algo (); - else if (!strcmp (string, "future-default")) + else if (!ascii_strcasecmp (string, "future-default") + || !ascii_strcasecmp (string, "futuredefault")) string = FUTURE_STD_KEY_PARAM; primary = xstrdup (string); @@ -3157,18 +3173,31 @@ parse_key_parameter_string (const char *string, int part, *secondary++ = 0; if (part == -1 || part == 0) { - err = parse_key_parameter_part (primary, 0, r_algo, r_size, + err = parse_key_parameter_part (primary, 0, 0, r_algo, r_size, r_keyuse, r_curve); if (!err && part == -1) - err = parse_key_parameter_part (secondary, 1, r_subalgo, r_subsize, + err = parse_key_parameter_part (secondary, 1, 0, r_subalgo, r_subsize, r_subkeyuse, r_subcurve); } else if (part == 1) { /* If we have SECONDARY, use that part. If there is only one - * part consider this to be the subkey algo. */ - err = parse_key_parameter_part (secondary? secondary : primary, 1, - r_algo, r_size, r_keyuse, r_curve); + * part consider this to be the subkey algo. In case a + * SUGGESTED_USE has been given and the usage of the secondary + * part does not match SUGGESTED_USE try again using the primary + * part. Noet thar when falling back to the primary key we need + * to force clearing the cert usage. */ + if (secondary) + { + err = parse_key_parameter_part (secondary, 1, 0, + r_algo, r_size, r_keyuse, r_curve); + if (!err && suggested_use && r_keyuse && !(suggested_use & *r_keyuse)) + err = parse_key_parameter_part (primary, 1, 1 /*(clear cert)*/, + r_algo, r_size, r_keyuse, r_curve); + } + else + err = parse_key_parameter_part (primary, 1, 0, + r_algo, r_size, r_keyuse, r_curve); } xfree (primary); @@ -3255,7 +3284,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key, * for the curve etc. That is a ugly but demanded for backward * compatibility with the batch key generation. It would be * better to make full use of parse_key_parameter_string. */ - parse_key_parameter_string (NULL, 0, + parse_key_parameter_string (NULL, 0, 0, &i, NULL, NULL, NULL, NULL, NULL, NULL, NULL); @@ -3977,9 +4006,10 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, if (!*expirestr || strcmp (expirestr, "-") == 0) expirestr = default_expiration_interval; - if ((!*algostr || !strcmp (algostr, "default") - || !strcmp (algostr, "future-default")) - && (!*usagestr || !strcmp (usagestr, "default") + if ((!*algostr || !ascii_strcasecmp (algostr, "default") + || !ascii_strcasecmp (algostr, "future-default") + || !ascii_strcasecmp (algostr, "futuredefault")) + && (!*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-"))) { /* Use default key parameters. */ @@ -3988,7 +4018,7 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, unsigned int keyuse, subkeyuse; const char *curve, *subcurve; - err = parse_key_parameter_string (algostr, -1, + err = parse_key_parameter_string (algostr, -1, 0, &algo, &size, &keyuse, &curve, &subalgo, &subsize, &subkeyuse, &subcurve); @@ -4367,7 +4397,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname, #endif , "--full-generate-key" ); - err = parse_key_parameter_string (NULL, -1, + err = parse_key_parameter_string (NULL, -1, 0, &algo, &size, &keyuse, &curve, &subalgo, &subsize, &subkeyuse, &subcurve); @@ -4914,6 +4944,7 @@ parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, } err = parse_key_parameter_string (algostr, for_subkey? 1 : 0, + usagestr? parse_usagestr (usagestr):0, &algo, &nbits, &use, &curve, NULL, NULL, NULL, NULL); if (err) @@ -4921,7 +4952,7 @@ parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, /* Parse the usage string. */ if (!usagestr || !*usagestr - || !strcmp (usagestr, "default") || !strcmp (usagestr, "-")) + || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-")) ; /* Keep usage from parse_key_parameter_string. */ else if ((wantuse = parse_usagestr (usagestr)) != -1) use = wantuse; diff --git a/g10/keyring.c b/g10/keyring.c index 50f1b82..25ef507 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -459,8 +459,8 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) break; /* Allowed by us. */ default: - log_error ("skipped packet of type %d in keyring\n", - (int)pkt->pkttype); + log_info ("skipped packet of type %d in keyring\n", + (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; diff --git a/g10/mainproc.c b/g10/mainproc.c index b712e60..512d33c 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -422,7 +422,7 @@ proc_pubkey_enc (ctrl_t ctrl, CTX c, PACKET *pkt) || have_secret_key_with_kid (enc->keyid))) { if(opt.list_only) - result = -1; + result = GPG_ERR_MISSING_ACTION; /* fixme: Use better error code. */ else { c->dek = xmalloc_secure_clear (sizeof *c->dek); @@ -440,9 +440,7 @@ proc_pubkey_enc (ctrl_t ctrl, CTX c, PACKET *pkt) else result = GPG_ERR_PUBKEY_ALGO; - if (result == -1) - ; - else + if (1) { /* Store it for later display. */ struct kidlist_item *x = xmalloc (sizeof *x); @@ -510,6 +508,10 @@ print_pkenc_list (ctrl_t ctrl, struct kidlist_item *list, int failed) write_status_text (STATUS_NO_SECKEY, buf); } } + else if (gpg_err_code (list->reason) == GPG_ERR_MISSING_ACTION) + { + /* Not tested for secret key due to --list-only mode. */ + } else if (list->reason) { log_info (_("public key decryption failed: %s\n"), diff --git a/g13/server.c b/g13/server.c index bbe42d4..defde6c 100644 --- a/g13/server.c +++ b/g13/server.c @@ -34,6 +34,7 @@ #include "mount.h" #include "suspend.h" #include "../common/server-help.h" +#include "../common/asshelp.h" #include "../common/call-gpg.h" @@ -737,24 +738,8 @@ g13_status (ctrl_t ctrl, int no, ...) } else { - assuan_context_t ctx = ctrl->server_local->assuan_ctx; - char buf[950], *p; - size_t n; - - p = buf; - n = 0; - while ( (text = va_arg (arg_ptr, const char *)) ) - { - if (n) - { - *p++ = ' '; - n++; - } - for ( ; *text && n < DIM (buf)-2; n++) - *p++ = *text++; - } - *p = 0; - err = assuan_write_status (ctx, get_status_string (no), buf); + err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx, + get_status_string (no), arg_ptr); } va_end (arg_ptr); diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c index b57369d..791e3b7 100644 --- a/g13/sh-cmd.c +++ b/g13/sh-cmd.c @@ -28,6 +28,7 @@ #include "g13-syshelp.h" #include #include "../common/i18n.h" +#include "../common/asshelp.h" #include "keyblob.h" @@ -904,34 +905,13 @@ sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen, gpg_error_t g13_status (ctrl_t ctrl, int no, ...) { - gpg_error_t err = 0; + gpg_error_t err; va_list arg_ptr; - const char *text; va_start (arg_ptr, no); - if (1) - { - assuan_context_t ctx = ctrl->server_local->assuan_ctx; - char buf[950], *p; - size_t n; - - p = buf; - n = 0; - while ( (text = va_arg (arg_ptr, const char *)) ) - { - if (n) - { - *p++ = ' '; - n++; - } - for ( ; *text && n < DIM (buf)-2; n++) - *p++ = *text++; - } - *p = 0; - err = assuan_write_status (ctx, get_status_string (no), buf); - } - + err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx, + get_status_string (no), arg_ptr); va_end (arg_ptr); return err; } diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index 9b43584..07774f2 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -464,7 +464,6 @@ main( int argc, char **argv ) /*create_dotlock(NULL); register locking cleanup */ /* We need to use the gcry malloc function because jnlib uses them. */ - keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h index fd331f1..be2dd72 100644 --- a/kbx/keybox-defs.h +++ b/kbx/keybox-defs.h @@ -33,16 +33,7 @@ #include /* off_t */ -/* We include the type definitions from jnlib instead of defining our - owns here. This will not allow us build KBX in a standalone way - but there is currently no need for it anyway. Same goes for - stringhelp.h which for example provides a replacement for stpcpy - - fixme: Better use the LIBOBJ mechnism. */ -#include "../common/types.h" -#include "../common/stringhelp.h" -#include "../common/dotlock.h" -#include "../common/logging.h" - +#include "../common/util.h" #include "keybox.h" @@ -209,64 +200,10 @@ int _keybox_dump_cut_records (const char *filename, unsigned long from, /*-- keybox-util.c --*/ -void *_keybox_malloc (size_t n); -void *_keybox_calloc (size_t n, size_t m); -void *_keybox_realloc (void *p, size_t n); -void _keybox_free (void *p); - -#define xtrymalloc(a) _keybox_malloc ((a)) -#define xtrycalloc(a,b) _keybox_calloc ((a),(b)) -#define xtryrealloc(a,b) _keybox_realloc((a),(b)) -#define xfree(a) _keybox_free ((a)) - - -#define DIM(v) (sizeof(v)/sizeof((v)[0])) -#define DIMof(type,member) DIM(((type *)0)->member) -#ifndef STR -# define STR(v) #v -#endif -#define STR2(v) STR(v) /* - a couple of handy macros -*/ - -#define return_if_fail(expr) do { \ - if (!(expr)) { \ - fprintf (stderr, "%s:%d: assertion '%s' failed\n", \ - __FILE__, __LINE__, #expr ); \ - return; \ - } } while (0) -#define return_null_if_fail(expr) do { \ - if (!(expr)) { \ - fprintf (stderr, "%s:%d: assertion '%s' failed\n", \ - __FILE__, __LINE__, #expr ); \ - return NULL; \ - } } while (0) -#define return_val_if_fail(expr,val) do { \ - if (!(expr)) { \ - fprintf (stderr, "%s:%d: assertion '%s' failed\n", \ - __FILE__, __LINE__, #expr ); \ - return (val); \ - } } while (0) -#define never_reached() do { \ - fprintf (stderr, "%s:%d: oops; should never get here\n", \ - __FILE__, __LINE__ ); \ - } while (0) - - -/* some macros to replace ctype ones and avoid locale problems */ -#define digitp(p) (*(p) >= '0' && *(p) <= '9') -#define hexdigitp(a) (digitp (a) \ - || (*(a) >= 'A' && *(a) <= 'F') \ - || (*(a) >= 'a' && *(a) <= 'f')) -/* the atoi macros assume that the buffer has only valid digits */ -#define atoi_1(p) (*(p) - '0' ) -#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) -#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) -#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ - *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) -#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + * A couple of handy macros + */ #endif /*KEYBOX_DEFS_H*/ diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index a5fc7fa..e309cce 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -247,7 +247,7 @@ blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr) if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20; - if (pos + keyinfolen*nkeys > length) + if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length) return 0; /* out of bounds */ for (idx=0; idx < nkeys; idx++) @@ -279,7 +279,7 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20; - if (pos + keyinfolen*nkeys > length) + if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length) return 0; /* out of bounds */ for (idx=0; idx < nkeys; idx++) @@ -313,7 +313,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx, if (keyinfolen < 28) return 0; /* invalid blob */ pos = 20 + keyinfolen*nkeys; - if (pos+2 > length) + if ((uint64_t)pos+2 > (uint64_t)length) return 0; /* out of bounds */ /*serial*/ @@ -340,7 +340,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx, mypos += idx*uidinfolen; off = get32 (buffer+mypos); len = get32 (buffer+mypos+4); - if (off+len > length) + if ((uint64_t)off+(uint64_t)len > (uint64_t)length) return 0; /* error: better stop here out of bounds */ if (len < 1) continue; /* empty name */ @@ -439,7 +439,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr, mypos += idx*uidinfolen; off = get32 (buffer+mypos); len = get32 (buffer+mypos+4); - if (off+len > length) + if ((uint64_t)off+(uint64_t)len > (uint64_t)length) return 0; /* error: better stop here - out of bounds */ if (x509) { @@ -522,7 +522,7 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip) return 0; /* Too short. */ cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); - if (cert_off+cert_len > length) + if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length) return 0; /* Too short. */ rc = ksba_reader_new (&reader); @@ -1097,7 +1097,7 @@ keybox_get_keyblock (KEYBOX_HANDLE hd, iobuf_t *r_iobuf, return gpg_error (GPG_ERR_TOO_SHORT); image_off = get32 (buffer+8); image_len = get32 (buffer+12); - if (image_off+image_len > length) + if ((uint64_t)image_off+(uint64_t)image_len > (uint64_t)length) return gpg_error (GPG_ERR_TOO_SHORT); err = _keybox_get_flag_location (buffer, length, KEYBOX_FLAG_SIG_INFO, @@ -1139,7 +1139,7 @@ keybox_get_cert (KEYBOX_HANDLE hd, ksba_cert_t *r_cert) return gpg_error (GPG_ERR_TOO_SHORT); cert_off = get32 (buffer+8); cert_len = get32 (buffer+12); - if (cert_off+cert_len > length) + if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length) return gpg_error (GPG_ERR_TOO_SHORT); rc = ksba_reader_new (&reader); diff --git a/kbx/keybox-util.c b/kbx/keybox-util.c index b71335b..3ce5162 100644 --- a/kbx/keybox-util.c +++ b/kbx/keybox-util.c @@ -27,52 +27,6 @@ #endif #include "keybox-defs.h" -#include "../common/utilproto.h" - - -static void *(*alloc_func)(size_t n) = malloc; -static void *(*realloc_func)(void *p, size_t n) = realloc; -static void (*free_func)(void*) = free; - - - -void -keybox_set_malloc_hooks ( void *(*new_alloc_func)(size_t n), - void *(*new_realloc_func)(void *p, size_t n), - void (*new_free_func)(void*) ) -{ - alloc_func = new_alloc_func; - realloc_func = new_realloc_func; - free_func = new_free_func; -} - -void * -_keybox_malloc (size_t n) -{ - return alloc_func (n); -} - -void * -_keybox_realloc (void *a, size_t n) -{ - return realloc_func (a, n); -} - -void * -_keybox_calloc (size_t n, size_t m) -{ - void *p = _keybox_malloc (n*m); - if (p) - memset (p, 0, n* m); - return p; -} - -void -_keybox_free (void *p) -{ - if (p) - free_func (p); -} /* Store the two malloced temporary file names used for keybox updates @@ -146,10 +100,3 @@ keybox_tmp_names (const char *filename, int for_keyring, *r_tmpname = tmp_name; return 0; } - -gpg_error_t -keybox_file_rename (const char *oldname, const char *newname, - int *block_signals) -{ - return gnupg_rename_file (oldname, newname, block_signals); -} diff --git a/kbx/keybox.h b/kbx/keybox.h index 29884b0..665b05f 100644 --- a/kbx/keybox.h +++ b/kbx/keybox.h @@ -127,10 +127,6 @@ int keybox_rebuild_cache (void *); /*-- keybox-util.c --*/ -void keybox_set_malloc_hooks ( void *(*new_alloc_func)(size_t n), - void *(*new_realloc_func)(void *p, size_t n), - void (*new_free_func)(void*) ); - gpg_error_t keybox_tmp_names (const char *filename, int for_keyring, char **r_bakname, char **r_tmpname); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 6fcec3e..f3065ed 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -121,6 +121,7 @@ static struct { /* V3.0 */ { 0x7F74, 0, 0, 1, 0, 0, 0, 0, "General Feature Management"}, { 0x00D5, 0, 0, 1, 0, 0, 0, 0, "AES key data"}, + { 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"}, { 0 } }; @@ -199,7 +200,7 @@ struct app_local_s { unsigned int private_dos:1; unsigned int algo_attr_change:1; /* Algorithm attributes changeable. */ unsigned int has_decrypt:1; /* Support symmetric decryption. */ - unsigned int kdf_do:1; /* Support KDF DOs. */ + unsigned int kdf_do:1; /* Support KDF DO. */ unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */ unsigned int pin_blk2:1; /* PIN block 2 format supported. */ @@ -347,7 +348,8 @@ get_cached_data (app_t app, int tag, err = iso7816_get_data (app->slot, exmode, tag, &p, &len); if (err) return err; - *result = p; + if (len) + *result = p; *resultlen = len; /* Check whether we should cache this object. */ @@ -369,7 +371,10 @@ get_cached_data (app_t app, int tag, c = xtrymalloc (sizeof *c + len); if (c) { - memcpy (c->data, p, len); + if (len) + memcpy (c->data, p, len); + else + xfree (p); c->length = len; c->tag = tag; c->next = app->app_local->cache; @@ -980,6 +985,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "PRIVATE-DO-4", 0x0104 }, { "$AUTHKEYID", 0x0000, -3 }, { "$DISPSERIALNO",0x0000, -4 }, + { "KDF", 0x00F9 }, { NULL, 0 } }; int idx, i, rc; @@ -2054,6 +2060,48 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining) return result; } +/* Compute hash if KDF-DO is available. CHVNO must be 0 for reset + code, 1 or 2 for user pin and 3 for admin pin. + */ +static gpg_error_t +pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) +{ + gpg_error_t err = 0; + void *relptr; + unsigned char *buffer; + size_t buflen; + + if (app->app_local->extcap.kdf_do + && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL)) + && buflen == 110 && (buffer[2] == 0x03)) + { + char *salt; + unsigned long s2k_count; + char dek[32]; + + salt = &buffer[(chvno==3 ? 34 : (chvno==0 ? 24 : 14))]; + s2k_count = (((unsigned int)buffer[8] << 24) + | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]); + err = gcry_kdf_derive (pinvalue, strlen (pinvalue), + GCRY_KDF_ITERSALTED_S2K, + DIGEST_ALGO_SHA256, salt, 8, + s2k_count, sizeof (dek), dek); + if (!err) + { + /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */ + *r_pinlen = 32; + memcpy (pinvalue, dek, *r_pinlen); + wipememory (dek, *r_pinlen); + } + + xfree (relptr); + } + else + *r_pinlen = strlen (pinvalue); + + return err; +} + /* Verify a CHV either using the pinentry or if possible by using a pinpad. PINCB and PINCB_ARG describe the usual callback @@ -2068,8 +2116,8 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining) static gpg_error_t verify_a_chv (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), - void *pincb_arg, - int chvno, unsigned long sigcount, char **pinvalue) + void *pincb_arg, int chvno, unsigned long sigcount, + char **pinvalue, int *pinlen) { int rc = 0; char *prompt_buffer = NULL; @@ -2081,6 +2129,7 @@ verify_a_chv (app_t app, log_assert (chvno == 1 || chvno == 2); *pinvalue = NULL; + *pinlen = 0; remaining = get_remaining_tries (app, 0); if (remaining == -1) @@ -2169,8 +2218,9 @@ verify_a_chv (app_t app, return gpg_error (GPG_ERR_BAD_PIN); } - rc = iso7816_verify (app->slot, 0x80+chvno, - *pinvalue, strlen (*pinvalue)); + rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen); + if (!rc) + rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen); } if (rc) @@ -2194,11 +2244,12 @@ verify_chv2 (app_t app, { int rc; char *pinvalue; + int pinlen; if (app->did_chv2) return 0; /* We already verified CHV2. */ - rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue); + rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen); if (rc) return rc; app->did_chv2 = 1; @@ -2209,7 +2260,7 @@ verify_chv2 (app_t app, the card is not configured to require a verification before each CHV1 controlled operation (force_chv1) and if we are not using the pinpad (PINVALUE == NULL). */ - rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) @@ -2319,6 +2370,7 @@ verify_chv3 (app_t app, else { char *pinvalue; + int pinlen; rc = pincb (pincb_arg, prompt, &pinvalue); xfree (prompt); @@ -2338,7 +2390,9 @@ verify_chv3 (app_t app, return gpg_error (GPG_ERR_BAD_PIN); } - rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); + if (!rc) + rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen); xfree (pinvalue); } @@ -2389,6 +2443,7 @@ do_setattr (app_t app, const char *name, { "SM-KEY-MAC", 0x00D2, 3, 0, 1 }, { "KEY-ATTR", 0, 0, 3, 1 }, { "AESKEY", 0x00D5, 3, 0, 1 }, + { "KDF", 0x00F9, 3, 0, 1 }, { NULL, 0 } }; int exmode; @@ -2501,6 +2556,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, pininfo_t pininfo; int use_pinpad = 0; int minlen = 6; + int pinlen0 = 0; + int pinlen = 0; (void)ctrl; memset (&pininfo, 0, sizeof pininfo); @@ -2685,10 +2742,17 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, rc = gpg_error_from_syserror (); else { - strcpy (stpcpy (buffer, resetcode), pinvalue); - rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, - buffer, strlen (buffer)); - wipememory (buffer, strlen (buffer)); + strcpy (buffer, resetcode); + rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0); + if (!rc) + { + strcpy (buffer+pinlen0, pinvalue); + rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen); + } + if (!rc) + rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, + buffer, pinlen0+pinlen); + wipememory (buffer, pinlen0 + pinlen); xfree (buffer); } } @@ -2700,16 +2764,19 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, rc = gpg_error (GPG_ERR_BAD_PIN); } else - rc = iso7816_put_data (app->slot, 0, 0xD3, - pinvalue, strlen (pinvalue)); + { + rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen); + if (!rc) + rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen); + } } else if (reset_mode) { - rc = iso7816_reset_retry_counter (app->slot, 0x81, - pinvalue, strlen (pinvalue)); + rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen); + if (!rc) + rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen); if (!rc && !app->app_local->extcap.is_v2) - rc = iso7816_reset_retry_counter (app->slot, 0x82, - pinvalue, strlen (pinvalue)); + rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen); } else if (!app->app_local->extcap.is_v2) { @@ -2750,14 +2817,20 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, - oldpinvalue, strlen (oldpinvalue), - pinvalue, strlen (pinvalue)); + { + rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0); + if (!rc) + rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen); + if (!rc) + rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, + oldpinvalue, pinlen0, + pinvalue, pinlen); + } } if (pinvalue) { - wipememory (pinvalue, strlen (pinvalue)); + wipememory (pinvalue, pinlen); xfree (pinvalue); } if (rc) @@ -2771,7 +2844,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, } if (oldpinvalue) { - wipememory (oldpinvalue, strlen (oldpinvalue)); + wipememory (oldpinvalue, pinlen0); xfree (oldpinvalue); } return rc; @@ -4277,8 +4350,9 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, if (!app->did_chv1 || app->force_chv1 ) { char *pinvalue; + int pinlen; - rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue); + rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen); if (rc) return rc; @@ -4291,7 +4365,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, pinpad has been used. */ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2) { - rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 4f4a32d..5046da5 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1778,6 +1778,7 @@ do_close_reader (ccid_driver_t handle) } libusb_free_transfer (handle->transfer); + handle->transfer = NULL; } libusb_release_interface (handle->idev, handle->ifc_no); --ccid_usb_thread_is_alive; @@ -2038,10 +2039,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, /* * Communication failure by device side. * Possibly, it was forcibly suspended and resumed. + * + * Only detect this kind of failure when interrupt transfer is + * not supported. For card reader with interrupt transfer + * support removal is detected by intr_cb. */ - DEBUGOUT ("CCID: card inactive/removed\n"); - if (handle->transfer == NULL) + if (handle->ep_intr < 0) { + DEBUGOUT ("CCID: card inactive/removed\n"); handle->powered_off = 1; scd_kick_the_loop (); } @@ -2539,6 +2544,14 @@ ccid_get_atr (ccid_driver_t handle, if (statusbits == 2) return CCID_DRIVER_ERR_NO_CARD; + /* + * In the first invocation of ccid_slot_status, card reader may + * return CCID_DRIVER_ERR_CARD_INACTIVE and handle->powered_off may + * become 1. Because inactive card is no problem (we are turning it + * ON here), clear the flag. + */ + handle->powered_off = 0; + /* For an inactive and also for an active card, issue the PowerOn command to get the ATR. */ again: diff --git a/scd/command.c b/scd/command.c index 6bcbce4..7011518 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1848,7 +1848,8 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...) p = buf; n = 0; - while ( (value = va_arg (arg_ptr, const unsigned char *)) ) + while ( (value = va_arg (arg_ptr, const unsigned char *)) + && n < DIM (buf)-2 ) { valuelen = va_arg (arg_ptr, size_t); if (!valuelen) @@ -1865,6 +1866,7 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...) { sprintf (p, "%%%02X", *value); p += 3; + n += 2; } else if (*value == ' ') *p++ = '+'; diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 3ad2657..cebeea9 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -236,6 +236,10 @@ static HANDLE the_event; /* PID to notify update of usb devices. */ static pid_t main_thread_pid; #endif +#ifdef HAVE_PSELECT_NO_EINTR +/* FD to notify changes. */ +static int notify_fd; +#endif static char *create_socket_name (char *standard_name); static gnupg_fd_t create_server_socket (const char *name, @@ -1210,6 +1214,8 @@ scd_kick_the_loop (void) if (ret == 0) log_error ("SetEvent for scd_kick_the_loop failed: %s\n", w32_strerror (-1)); +#elif defined(HAVE_PSELECT_NO_EINTR) + write (notify_fd, "", 1); #else ret = kill (main_thread_pid, SIGCONT); if (ret < 0) @@ -1241,6 +1247,17 @@ handle_connections (int listen_fd) #else int signo; #endif +#ifdef HAVE_PSELECT_NO_EINTR + int pipe_fd[2]; + + ret = gnupg_create_pipe (pipe_fd); + if (ret) + { + log_error ("pipe creation failed: %s\n", gpg_strerror (ret)); + return; + } + notify_fd = pipe_fd[1]; +#endif ret = npth_attr_init(&tattr); if (ret) @@ -1298,6 +1315,7 @@ handle_connections (int listen_fd) for (;;) { int periodical_check; + int max_fd = nfd; if (shutdown_pending) { @@ -1326,8 +1344,14 @@ handle_connections (int listen_fd) thus a simple assignment is fine to copy the entire set. */ read_fdset = fdset; +#ifdef HAVE_PSELECT_NO_EINTR + FD_SET (pipe_fd[0], &read_fdset); + if (max_fd < pipe_fd[0]) + max_fd = pipe_fd[0]; +#endif + #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t, + ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t, npth_sigev_sigmask ()); saved_errno = errno; @@ -1353,6 +1377,15 @@ handle_connections (int listen_fd) /* Timeout. Will be handled when calculating the next timeout. */ continue; +#ifdef HAVE_PSELECT_NO_EINTR + if (FD_ISSET (pipe_fd[0], &read_fdset)) + { + char buf[256]; + + read (pipe_fd[0], buf, sizeof buf); + } +#endif + if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) { ctrl_t ctrl; @@ -1394,6 +1427,10 @@ handle_connections (int listen_fd) if (the_event != INVALID_HANDLE_VALUE) CloseHandle (the_event); #endif +#ifdef HAVE_PSELECT_NO_EINTR + close (pipe_fd[0]); + close (pipe_fd[1]); +#endif cleanup (); log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); npth_attr_destroy (&tattr); diff --git a/sm/export.c b/sm/export.c index 29a5ac3..7bea9cc 100644 --- a/sm/export.c +++ b/sm/export.c @@ -479,6 +479,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode) leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); + xfree (keygrip); xfree (desc); keydb_release (hd); } @@ -603,7 +604,7 @@ sexp_to_kparms (gcry_sexp_t sexp) array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */ gcry_mpi_sub_ui (array[6], array[4], 1); - gcry_mpi_mod (array[6], array[3], array[6]); + gcry_mpi_mod (array[6], array[2], array[6]); return array; } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index b505be1..ab08a52 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1007,8 +1007,6 @@ main ( int argc, char **argv) assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); setup_libassuan_logging (&opt.debug, NULL); - keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); - /* Setup a default control structure for command line mode */ memset (&ctrl, 0, sizeof ctrl); gpgsm_init_default_ctrl (&ctrl); diff --git a/sm/server.c b/sm/server.c index 568e51b..721f3fa 100644 --- a/sm/server.c +++ b/sm/server.c @@ -31,6 +31,7 @@ #include #include "../common/sysutils.h" #include "../common/server-help.h" +#include "../common/asshelp.h" #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) @@ -1426,24 +1427,8 @@ gpgsm_status2 (ctrl_t ctrl, int no, ...) } else { - assuan_context_t ctx = ctrl->server_local->assuan_ctx; - char buf[950], *p; - size_t n; - - p = buf; - n = 0; - while ( (text = va_arg (arg_ptr, const char *)) ) - { - if (n) - { - *p++ = ' '; - n++; - } - for ( ; *text && n < DIM (buf)-2; n++) - *p++ = *text++; - } - *p = 0; - err = assuan_write_status (ctx, get_status_string (no), buf); + err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx, + get_status_string (no), arg_ptr); } va_end (arg_ptr); diff --git a/tests/asschk.c b/tests/asschk.c index 2595c0a..65828e5 100644 --- a/tests/asschk.c +++ b/tests/asschk.c @@ -116,7 +116,7 @@ #endif #if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 +# if __GNUC__ >= 2 && !defined (__func__) # define __func__ __FUNCTION__ # else /* Let's try our luck here. Some systems may provide __func__ without diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index 73a8a1f..73945ff 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -325,119 +325,6 @@ main (int argc, char **argv) -struct get_key_status_parm_s -{ - const char *fpr; - int found; - int count; -}; - -static void -get_key_status_cb (void *opaque, const char *keyword, char *args) -{ - struct get_key_status_parm_s *parm = opaque; - - /*log_debug ("%s: %s\n", keyword, args);*/ - if (!strcmp (keyword, "EXPORTED")) - { - parm->count++; - if (!ascii_strcasecmp (args, parm->fpr)) - parm->found = 1; - } -} - - -/* Get a key by fingerprint from gpg's keyring and make sure that the - * mail address ADDRSPEC is included in the key. If EXACT is set the - * returned user id must match Addrspec exactly and not just in the - * addr-spec (mailbox) part. The key is returned as a new memory - * stream at R_KEY. */ -static gpg_error_t -get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, - int exact) -{ - gpg_error_t err; - ccparray_t ccp; - const char **argv = NULL; - estream_t key = NULL; - struct get_key_status_parm_s parm; - char *filterexp = NULL; - - memset (&parm, 0, sizeof parm); - - *r_key = NULL; - - key = es_fopenmem (0, "w+b"); - if (!key) - { - err = gpg_error_from_syserror (); - log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); - goto leave; - } - - /* Prefix the key with the MIME content type. */ - es_fputs ("Content-Type: application/pgp-keys\n" - "\n", key); - - filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec); - if (!filterexp) - { - err = gpg_error_from_syserror (); - log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); - goto leave; - } - - ccparray_init (&ccp, 0); - - ccparray_put (&ccp, "--no-options"); - if (!opt.verbose) - ccparray_put (&ccp, "--quiet"); - else if (opt.verbose > 1) - ccparray_put (&ccp, "--verbose"); - ccparray_put (&ccp, "--batch"); - ccparray_put (&ccp, "--status-fd=2"); - ccparray_put (&ccp, "--always-trust"); - ccparray_put (&ccp, "--armor"); - ccparray_put (&ccp, "--export-options=export-minimal"); - ccparray_put (&ccp, "--export-filter"); - ccparray_put (&ccp, filterexp); - ccparray_put (&ccp, "--export"); - ccparray_put (&ccp, "--"); - ccparray_put (&ccp, fingerprint); - - ccparray_put (&ccp, NULL); - argv = ccparray_get (&ccp, NULL); - if (!argv) - { - err = gpg_error_from_syserror (); - goto leave; - } - parm.fpr = fingerprint; - err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL, - NULL, key, - get_key_status_cb, &parm); - if (!err && parm.count > 1) - err = gpg_error (GPG_ERR_TOO_MANY); - else if (!err && !parm.found) - err = gpg_error (GPG_ERR_NOT_FOUND); - if (err) - { - log_error ("export failed: %s\n", gpg_strerror (err)); - goto leave; - } - - es_rewind (key); - *r_key = key; - key = NULL; - - leave: - es_fclose (key); - xfree (argv); - xfree (filterexp); - return err; -} - - /* Add the user id UID to the key identified by FINGERPRINT. */ static gpg_error_t add_user_id (const char *fingerprint, const char *uid) @@ -767,7 +654,7 @@ command_send (const char *fingerprint, const char *userid) err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } - err = get_key (&key, fingerprint, addrspec, 0); + err = wks_get_key (&key, fingerprint, addrspec, 0); if (err) goto leave; @@ -782,27 +669,19 @@ command_send (const char *fingerprint, const char *userid) err = 0; } else - err = wkd_get_submission_address (addrspec, &submission_to); - if (err) - { - log_error (_("error looking up submission address for domain '%s': %s\n"), - domain, gpg_strerror (err)); - if (gpg_err_code (err) == GPG_ERR_NO_DATA) - log_error (_("this domain probably doesn't support WKS.\n")); - goto leave; - } - log_info ("submitting request to '%s'\n", submission_to); - - /* Get the policy flags. */ - if (!fake_submission_addr) { + /* We first try to get the submission address from the policy + * file (this is the new method). If both are available we + * check that they match and print a warning if not. In the + * latter case we keep on using the one from the + * submission-address file. */ estream_t mbuf; err = wkd_get_policy_flags (addrspec, &mbuf); if (err && gpg_err_code (err) != GPG_ERR_NO_DATA) { log_error ("error reading policy flags for '%s': %s\n", - submission_to, gpg_strerror (err)); + domain, gpg_strerror (err)); goto leave; } if (mbuf) @@ -812,8 +691,35 @@ command_send (const char *fingerprint, const char *userid) if (err) goto leave; } + + err = wkd_get_submission_address (addrspec, &submission_to); + if (err && !policy.submission_address) + { + log_error (_("error looking up submission address for domain '%s'" + ": %s\n"), domain, gpg_strerror (err)); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + log_error (_("this domain probably doesn't support WKS.\n")); + goto leave; + } + + if (submission_to && policy.submission_address + && ascii_strcasecmp (submission_to, policy.submission_address)) + log_info ("Warning: different submission addresses (sa=%s, po=%s)\n", + submission_to, policy.submission_address); + + if (!submission_to) + { + submission_to = xtrystrdup (policy.submission_address); + if (!submission_to) + { + err = gpg_error_from_syserror (); + goto leave; + } + } } + log_info ("submitting request to '%s'\n", submission_to); + if (policy.auth_submit) log_info ("no confirmation required for '%s'\n", addrspec); @@ -853,7 +759,7 @@ command_send (const char *fingerprint, const char *userid) estream_t newkey; es_rewind (key); - err = wks_filter_uid (&newkey, key, thisuid->uid); + err = wks_filter_uid (&newkey, key, thisuid->uid, 0); if (err) { log_error ("error filtering key: %s\n", gpg_strerror (err)); @@ -878,7 +784,7 @@ command_send (const char *fingerprint, const char *userid) * the key again. */ es_fclose (key); key = NULL; - err = get_key (&key, fingerprint, addrspec, 1); + err = wks_get_key (&key, fingerprint, addrspec, 1); if (err) goto leave; } @@ -1002,6 +908,7 @@ command_send (const char *fingerprint, const char *userid) free_uidinfo_list (uidlist); es_fclose (keyenc); es_fclose (key); + wks_free_policy (&policy); xfree (addrspec); return err; } diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c index 0b1d642..a588155 100644 --- a/tools/gpg-wks-server.c +++ b/tools/gpg-wks-server.c @@ -1,5 +1,5 @@ /* gpg-wks-server.c - A server for the Web Key Service protocols. - * Copyright (C) 2016 Werner Koch + * Copyright (C) 2016, 2018 Werner Koch * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. @@ -20,7 +20,7 @@ /* The Web Key Service I-D defines an update protocol to store a * public key in the Web Key Directory. The current specification is - * draft-koch-openpgp-webkey-service-01.txt. + * draft-koch-openpgp-webkey-service-05.txt. */ #include @@ -35,6 +35,7 @@ #include "../common/util.h" #include "../common/init.h" #include "../common/sysutils.h" +#include "../common/userids.h" #include "../common/ccparray.h" #include "../common/exectool.h" #include "../common/zb32.h" @@ -154,7 +155,7 @@ static gpg_error_t command_receive_cb (void *opaque, const char *mediatype, estream_t fp, unsigned int flags); static gpg_error_t command_list_domains (void); -static gpg_error_t command_install_key (const char *fname); +static gpg_error_t command_install_key (const char *fname, const char *userid); static gpg_error_t command_remove_key (const char *mailaddr); static gpg_error_t command_revoke_key (const char *mailaddr); static gpg_error_t command_check_key (const char *mailaddr); @@ -376,9 +377,9 @@ main (int argc, char **argv) break; case aInstallKey: - if (argc != 1) - wrong_args ("--install-key FILE"); - err = command_install_key (*argv); + if (argc != 2) + wrong_args ("--install-key FILE USER-ID"); + err = command_install_key (*argv, argv[1]); break; case aRemoveKey: @@ -1135,6 +1136,8 @@ process_new_key (server_ctx_t ctx, estream_t key) char *fname = NULL; struct policy_flags_s policybuf; + memset (&policybuf, 0, sizeof policybuf); + /* First figure out the user id from the key. */ xfree (ctx->fpr); free_uidinfo_list (ctx->mboxes); @@ -1206,6 +1209,7 @@ process_new_key (server_ctx_t ctx, estream_t key) xfree (nonce); xfree (fname); xfree (dname); + wks_free_policy (&policybuf); return err; } @@ -1336,6 +1340,81 @@ send_congratulation_message (const char *mbox, const char *keyfile) } +/* Write the content of SRC to the new file FNAME. */ +static gpg_error_t +write_to_file (estream_t src, const char *fname) +{ + gpg_error_t err; + estream_t dst; + char buffer[4096]; + size_t nread, written; + + dst = es_fopen (fname, "wb"); + if (!dst) + return gpg_error_from_syserror (); + + do + { + nread = es_fread (buffer, 1, sizeof buffer, src); + if (!nread) + break; + written = es_fwrite (buffer, 1, nread, dst); + if (written != nread) + break; + } + while (!es_feof (src) && !es_ferror (src) && !es_ferror (dst)); + if (!es_feof (src) || es_ferror (src) || es_ferror (dst)) + { + err = gpg_error_from_syserror (); + es_fclose (dst); + gnupg_remove (fname); + return err; + } + + if (es_fclose (dst)) + { + err = gpg_error_from_syserror (); + log_error ("error closing '%s': %s\n", fname, gpg_strerror (err)); + return err; + } + + return 0; +} + + +/* Compute the the full file name for the key with ADDRSPEC and return + * it at R_FNAME. */ +static gpg_error_t +compute_hu_fname (char **r_fname, const char *addrspec) +{ + gpg_error_t err; + char *hash; + const char *domain; + char sha1buf[20]; + + *r_fname = NULL; + + domain = strchr (addrspec, '@'); + if (!domain || !domain[1] || domain == addrspec) + return gpg_error (GPG_ERR_INV_ARG); + domain++; + + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, addrspec, domain - addrspec - 1); + hash = zb32_encode (sha1buf, 8*20); + if (!hash) + return gpg_error_from_syserror (); + + *r_fname = make_filename_try (opt.directory, domain, "hu", hash, NULL); + if (!*r_fname) + err = gpg_error_from_syserror (); + else + err = 0; + + xfree (hash); + return err; +} + + /* Check that we have send a request with NONCE and publish the key. */ static gpg_error_t check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) @@ -1409,24 +1488,10 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) goto leave; } - /* Hash user ID and create filename. */ - s = strchr (address, '@'); - log_assert (s); - gcry_md_hash_buffer (GCRY_MD_SHA1, shaxbuf, address, s - address); - hash = zb32_encode (shaxbuf, 8*20); - if (!hash) - { - err = gpg_error_from_syserror (); - goto leave; - } - - fnewname = make_filename_try (opt.directory, domain, "hu", hash, NULL); - if (!fnewname) - { - err = gpg_error_from_syserror (); - goto leave; - } + err = compute_hu_fname (&fnewname, address); + if (err) + goto leave; /* Publish. */ err = copy_key_as_binary (fname, fnewname, address); @@ -1897,6 +1962,7 @@ command_list_domains (void) if (!memcmp (&empty_policy, &policy, sizeof policy)) log_error ("domain %s: empty policy file\n", domain); } + wks_free_policy (&policy); } @@ -1931,16 +1997,140 @@ command_cron (void) } -/* Install a single key into the WKD by reading FNAME. */ +/* Install a single key into the WKD by reading FNAME and extracting + * USERID. */ static gpg_error_t -command_install_key (const char *fname) +command_install_key (const char *fname, const char *userid) { - (void)fname; - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + estream_t fp = NULL; + char *addrspec = NULL; + char *fpr = NULL; + uidinfo_list_t uidlist = NULL; + uidinfo_list_t uid, thisuid; + time_t thistime; + char *huname = NULL; + int any; + + addrspec = mailbox_from_userid (userid); + if (!addrspec) + { + log_error ("\"%s\" is not a proper mail address\n", userid); + err = gpg_error (GPG_ERR_INV_USER_ID); + goto leave; + } + + if (!classify_user_id (fname, &desc, 1) + && (desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) + { + /* FNAME looks like a fingerprint. Get the key from the + * standard keyring. */ + err = wks_get_key (&fp, fname, addrspec, 0); + if (err) + { + log_error ("error getting key '%s' (uid='%s'): %s\n", + fname, addrspec, gpg_strerror (err)); + goto leave; + } + } + else /* Take it from the file */ + { + fp = es_fopen (fname, "rb"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("error reading '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + } + + /* List the key so that we can figure out the newest UID with the + * requested addrspec. */ + err = wks_list_key (fp, &fpr, &uidlist); + if (err) + { + log_error ("error parsing key: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + thistime = 0; + thisuid = NULL; + any = 0; + for (uid = uidlist; uid; uid = uid->next) + { + if (!uid->mbox) + continue; /* Should not happen anyway. */ + if (ascii_strcasecmp (uid->mbox, addrspec)) + continue; /* Not the requested addrspec. */ + any = 1; + if (uid->created > thistime) + { + thistime = uid->created; + thisuid = uid; + } + } + if (!thisuid) + thisuid = uidlist; /* This is the case for a missing timestamp. */ + if (!any) + { + log_error ("public key in '%s' has no mail address '%s'\n", + fname, addrspec); + err = gpg_error (GPG_ERR_INV_USER_ID); + goto leave; + } + + if (opt.verbose) + log_info ("using key with user id '%s'\n", thisuid->uid); + + { + estream_t fp2; + + es_rewind (fp); + err = wks_filter_uid (&fp2, fp, thisuid->uid, 1); + if (err) + { + log_error ("error filtering key: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + es_fclose (fp); + fp = fp2; + } + + /* Hash user ID and create filename. */ + err = compute_hu_fname (&huname, addrspec); + if (err) + goto leave; + + /* Publish. */ + err = write_to_file (fp, huname); + if (err) + { + log_error ("copying key to '%s' failed: %s\n", huname,gpg_strerror (err)); + goto leave; + } + + /* Make sure it is world readable. */ + if (gnupg_chmod (huname, "-rwxr--r--")) + log_error ("can't set permissions of '%s': %s\n", + huname, gpg_strerror (gpg_err_code_from_syserror())); + + if (!opt.quiet) + log_info ("key %s published for '%s'\n", fpr, addrspec); + + leave: + xfree (huname); + free_uidinfo_list (uidlist); + xfree (fpr); + xfree (addrspec); + es_fclose (fp); + return err; } -/* Return the filename and optioanlly the addrspec for USERID at +/* Return the filename and optionally the addrspec for USERID at * R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. */ static gpg_error_t fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index ece7add..1b91b65 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -60,6 +60,7 @@ struct /* The parsed policy flags. */ struct policy_flags_s { + char *submission_address; unsigned int mailbox_only : 1; unsigned int dane_only : 1; unsigned int auth_submit : 1; @@ -85,13 +86,16 @@ typedef struct uidinfo_list_s *uidinfo_list_t; void wks_set_status_fd (int fd); void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void free_uidinfo_list (uidinfo_list_t list); +gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint, + const char *addrspec, int exact); gpg_error_t wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes); gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key, - const char *uid); + const char *uid, int binary); gpg_error_t wks_send_mime (mime_maker_t mime); gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown); +void wks_free_policy (policy_flags_t policy); /*-- wks-receive.c --*/ diff --git a/tools/wks-util.c b/tools/wks-util.c index 889ca36..3fd824c 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -133,6 +133,120 @@ free_uidinfo_list (uidinfo_list_t list) +struct get_key_status_parm_s +{ + const char *fpr; + int found; + int count; +}; + + +static void +get_key_status_cb (void *opaque, const char *keyword, char *args) +{ + struct get_key_status_parm_s *parm = opaque; + + /*log_debug ("%s: %s\n", keyword, args);*/ + if (!strcmp (keyword, "EXPORTED")) + { + parm->count++; + if (!ascii_strcasecmp (args, parm->fpr)) + parm->found = 1; + } +} + +/* Get a key by fingerprint from gpg's keyring and make sure that the + * mail address ADDRSPEC is included in the key. If EXACT is set the + * returned user id must match Addrspec exactly and not just in the + * addr-spec (mailbox) part. The key is returned as a new memory + * stream at R_KEY. */ +gpg_error_t +wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, + int exact) +{ + gpg_error_t err; + ccparray_t ccp; + const char **argv = NULL; + estream_t key = NULL; + struct get_key_status_parm_s parm; + char *filterexp = NULL; + + memset (&parm, 0, sizeof parm); + + *r_key = NULL; + + key = es_fopenmem (0, "w+b"); + if (!key) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Prefix the key with the MIME content type. */ + es_fputs ("Content-Type: application/pgp-keys\n" + "\n", key); + + filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec); + if (!filterexp) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); + goto leave; + } + + ccparray_init (&ccp, 0); + + ccparray_put (&ccp, "--no-options"); + if (!opt.verbose) + ccparray_put (&ccp, "--quiet"); + else if (opt.verbose > 1) + ccparray_put (&ccp, "--verbose"); + ccparray_put (&ccp, "--batch"); + ccparray_put (&ccp, "--status-fd=2"); + ccparray_put (&ccp, "--always-trust"); + ccparray_put (&ccp, "--armor"); + ccparray_put (&ccp, "--export-options=export-minimal"); + ccparray_put (&ccp, "--export-filter"); + ccparray_put (&ccp, filterexp); + ccparray_put (&ccp, "--export"); + ccparray_put (&ccp, "--"); + ccparray_put (&ccp, fingerprint); + + ccparray_put (&ccp, NULL); + argv = ccparray_get (&ccp, NULL); + if (!argv) + { + err = gpg_error_from_syserror (); + goto leave; + } + parm.fpr = fingerprint; + err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL, + NULL, key, + get_key_status_cb, &parm); + if (!err && parm.count > 1) + err = gpg_error (GPG_ERR_TOO_MANY); + else if (!err && !parm.found) + err = gpg_error (GPG_ERR_NOT_FOUND); + if (err) + { + log_error ("export failed: %s\n", gpg_strerror (err)); + goto leave; + } + + es_rewind (key); + *r_key = key; + key = NULL; + + leave: + es_fclose (key); + xfree (argv); + xfree (filterexp); + return err; +} + + + /* Helper for wks_list_key and wks_filter_uid. */ static void key_status_cb (void *opaque, const char *keyword, char *args) @@ -317,10 +431,13 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) /* Run gpg as a filter on KEY and write the output to a new stream - * stored at R_NEWKEY. The new key will containn only the user id - * UID. Returns 0 on success. Only one key is expected in KEY. */ + * stored at R_NEWKEY. The new key will contain only the user id UID. + * Returns 0 on success. Only one key is expected in KEY. If BINARY + * is set the resulting key is returned as a binary (non-armored) + * keyblock. */ gpg_error_t -wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid) +wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid, + int binary) { gpg_error_t err; ccparray_t ccp; @@ -340,8 +457,9 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid) } /* Prefix the key with the MIME content type. */ - es_fputs ("Content-Type: application/pgp-keys\n" - "\n", newkey); + if (!binary) + es_fputs ("Content-Type: application/pgp-keys\n" + "\n", newkey); filterexp = es_bsprintf ("keep-uid=uid=%s", uid); if (!filterexp) @@ -361,7 +479,8 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid) ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--always-trust"); - ccparray_put (&ccp, "--armor"); + if (!binary) + ccparray_put (&ccp, "--armor"); ccparray_put (&ccp, "--import-options=import-export"); ccparray_put (&ccp, "--import-filter"); ccparray_put (&ccp, filterexp); @@ -443,6 +562,7 @@ gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) { enum tokens { + TOK_SUBMISSION_ADDRESS, TOK_MAILBOX_ONLY, TOK_DANE_ONLY, TOK_AUTH_SUBMIT, @@ -453,6 +573,7 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) const char *name; enum tokens token; } keywords[] = { + { "submission-address", TOK_SUBMISSION_ADDRESS }, { "mailbox-only", TOK_MAILBOX_ONLY }, { "dane-only", TOK_DANE_ONLY }, { "auth-submit", TOK_AUTH_SUBMIT }, @@ -519,6 +640,20 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) switch (keywords[i].token) { + case TOK_SUBMISSION_ADDRESS: + if (!value || !*value) + { + err = gpg_error (GPG_ERR_SYNTAX); + goto leave; + } + xfree (flags->submission_address); + flags->submission_address = xtrystrdup (value); + if (!flags->submission_address) + { + err = gpg_error_from_syserror (); + goto leave; + } + break; case TOK_MAILBOX_ONLY: flags->mailbox_only = 1; break; case TOK_DANE_ONLY: flags->dane_only = 1; break; case TOK_AUTH_SUBMIT: flags->auth_submit = 1; break; @@ -553,3 +688,14 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) return err; } + + +void +wks_free_policy (policy_flags_t policy) +{ + if (policy) + { + xfree (policy->submission_address); + memset (policy, 0, sizeof *policy); + } +} -- 2.7.4