Merge branch 'STABLE-BRANCH-2-2'
authorWerner Koch <wk@gnupg.org>
Thu, 22 Feb 2018 15:19:56 +0000 (16:19 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 22 Feb 2018 15:19:56 +0000 (16:19 +0100)
30 files changed:
AUTHORS
README
agent/command.c
build-aux/speedo.mk
common/argparse.c
common/asshelp.h
common/asshelp2.c
common/w32info-rc.h.in
configure.ac
dirmngr/server.c
doc/examples/vsnfd.prf
doc/gpg.texi
doc/gpgsm.texi
doc/wks.texi
g10/keydb.c
g10/keyedit.c
g10/keyring.c
g13/server.c
g13/sh-cmd.c
kbx/keybox-search.c
scd/app-openpgp.c
scd/command.c
scd/scdaemon.c
sm/export.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 fd215d2..0dabbc1 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
@@ -264,8 +264,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/README b/README
index f4b549c..39ccc4d 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 7c7e8a4..e2486a5 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 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 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 6268766..f680658 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.
 #
@@ -650,6 +650,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
@@ -726,10 +727,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
@@ -831,7 +842,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}"
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 3a2c0ff..8fea489 100644 (file)
@@ -3829,6 +3829,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 b187a54..37a5353 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 131a4c2..4508ae2 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:
@@ -339,17 +345,13 @@ The output of the last command looks similar to this:
   ssb   rsa3072 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 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 3ae96a3..2c33a29 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 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 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 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 fb869b2..54f04c6 100644 (file)
@@ -348,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. */
@@ -370,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;
@@ -2068,7 +2072,8 @@ pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
   size_t buflen;
 
   if (app->app_local->extcap.kdf_do
-      && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL)))
+      && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL))
+      && buflen == 110 && (buffer[2] == 0x03))
     {
       char *salt;
       unsigned long s2k_count;
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 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);
+    }
+}