Imported Upstream version 2.2.5 upstream/2.2.5
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 9 Feb 2021 07:00:32 +0000 (16:00 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 9 Feb 2021 07:00:32 +0000 (16:00 +0900)
46 files changed:
AUTHORS
NEWS
README
agent/command-ssh.c
agent/command.c
autogen.rc
autogen.sh
build-aux/speedo.mk
common/argparse.c
common/asshelp.h
common/asshelp2.c
common/homedir.c
common/logging.h
common/w32info-rc.h.in
configure.ac
dirmngr/server.c
doc/examples/vsnfd.prf
doc/gpg.texi
doc/gpgsm.texi
doc/wks.texi
g10/card-util.c
g10/getkey.c
g10/keydb.c
g10/keyedit.c
g10/keygen.c
g10/keyring.c
g10/mainproc.c
g13/server.c
g13/sh-cmd.c
kbx/kbxutil.c
kbx/keybox-defs.h
kbx/keybox-search.c
kbx/keybox-util.c
kbx/keybox.h
scd/app-openpgp.c
scd/ccid-driver.c
scd/command.c
scd/scdaemon.c
sm/export.c
sm/gpgsm.c
sm/server.c
tests/asschk.c
tools/gpg-wks-client.c
tools/gpg-wks-server.c
tools/gpg-wks.h
tools/wks-util.c

diff --git a/AUTHORS b/AUTHORS
index d27dfb6..473b94c 100644 (file)
--- 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 <drepper@gnu.ai.mit.edu>
   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 (file)
--- 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 (file)
--- 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
index 9d45a18..e0b7238 100644 (file)
@@ -44,6 +44,9 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #endif /*!HAVE_W32_SYSTEM*/
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
 #ifdef HAVE_UCRED_H
 #include <ucred.h>
 #endif
index 0916f88..f9bc6ca 100644 (file)
@@ -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);
             }
         }
index 6835929..3ea03e0 100644 (file)
@@ -1,5 +1,7 @@
 # autogen.sh configuration for GnuPG                           -*- sh -*-
 
+display_name=GnuPG
+
 #version_parts=3
 
 case "$myhost:$myhostsub" in
index e5ba5bf..b238550 100755 (executable)
@@ -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
 
index 2b3b72b..320d440 100644 (file)
@@ -63,6 +63,7 @@ help:
        @echo 'You may append INSTALL_PREFIX=<dir> 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
 
index f5e4ceb..90d0ff7 100644 (file)
@@ -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*/
 
index f169d87..bf1bd17 100644 (file)
@@ -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*/
index f85c1e6..0a7c454 100644 (file)
@@ -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;
+}
index a598900..65cf50f 100644 (file)
@@ -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;
 
index e1bf56b..2225100 100644 (file)
@@ -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*/
index 4e46b97..2ff6863 100644 (file)
@@ -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"
index 382ef1d..e249361 100644 (file)
@@ -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
 
index 3d0768b..60d9802 100644 (file)
@@ -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);
index e8732de..1dc21e0 100644 (file)
@@ -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
index 35bb9a8..ddebc69 100644 (file)
@@ -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
 
index 5d79ce5..cd2a741 100644 (file)
@@ -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}
index 7f7d515..6d62282 100644 (file)
@@ -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"
index a396b7d..759dde8 100644 (file)
@@ -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,
index e31e023..dabd052 100644 (file)
@@ -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)
        {
index 58a14a8..03fadfd 100644 (file)
@@ -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;
index 4acb2de..17cf7d6 100644 (file)
@@ -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)
                   {
index b42afa8..8de6538 100644 (file)
@@ -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;
index 50f1b82..25ef507 100644 (file)
@@ -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;
index b712e60..512d33c 100644 (file)
@@ -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"),
index bbe42d4..defde6c 100644 (file)
@@ -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);
index b57369d..791e3b7 100644 (file)
@@ -28,6 +28,7 @@
 #include "g13-syshelp.h"
 #include <assuan.h>
 #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;
 }
index 9b43584..07774f2 100644 (file)
@@ -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 );
 
 
index fd331f1..be2dd72 100644 (file)
 
 #include <sys/types.h> /* 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*/
index a5fc7fa..e309cce 100644 (file)
@@ -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);
index b71335b..3ce5162 100644 (file)
 #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);
-}
index 29884b0..665b05f 100644 (file)
@@ -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);
 
index 6fcec3e..f3065ed 100644 (file)
@@ -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)
index 4f4a32d..5046da5 100644 (file)
@@ -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:
index 6bcbce4..7011518 100644 (file)
@@ -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++ = '+';
index 3ad2657..cebeea9 100644 (file)
@@ -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
 \f
 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);
index 29a5ac3..7bea9cc 100644 (file)
@@ -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;
 }
index b505be1..ab08a52 100644 (file)
@@ -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);
index 568e51b..721f3fa 100644 (file)
@@ -31,6 +31,7 @@
 #include <assuan.h>
 #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);
index 2595c0a..65828e5 100644 (file)
 #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
index 73a8a1f..73945ff 100644 (file)
@@ -325,119 +325,6 @@ main (int argc, char **argv)
 
 
 \f
-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;
 }
index 0b1d642..a588155 100644 (file)
@@ -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 <config.h>
@@ -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)
index ece7add..1b91b65 100644 (file)
@@ -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 --*/
 
index 889ca36..3fd824c 100644 (file)
@@ -133,6 +133,120 @@ free_uidinfo_list (uidinfo_list_t list)
 
 
 \f
+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;
+}
+
+
+\f
 /* 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);
+    }
+}