summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2018-02-22 16:19:56 +0100
committerWerner Koch <wk@gnupg.org>2018-02-22 16:19:56 +0100
commit20539ea5cad1903352e01ef95aecdda4d5cd999b (patch)
tree609042662948bcd256dd4400b64bdb5e656173e4
parentbuild: Update autogen.sh to set a git PATCH prefix. (diff)
parentPost release updates. (diff)
downloadgnupg2-20539ea5cad1903352e01ef95aecdda4d5cd999b.tar.xz
gnupg2-20539ea5cad1903352e01ef95aecdda4d5cd999b.zip
Merge branch 'STABLE-BRANCH-2-2'
-rw-r--r--AUTHORS10
-rw-r--r--README4
-rw-r--r--agent/command.c46
-rw-r--r--build-aux/speedo.mk27
-rw-r--r--common/argparse.c2
-rw-r--r--common/asshelp.h7
-rw-r--r--common/asshelp2.c63
-rw-r--r--common/w32info-rc.h.in2
-rw-r--r--configure.ac20
-rw-r--r--dirmngr/server.c28
-rw-r--r--doc/examples/vsnfd.prf1
-rw-r--r--doc/gpg.texi13
-rw-r--r--doc/gpgsm.texi6
-rw-r--r--doc/wks.texi26
-rw-r--r--g10/keydb.c5
-rw-r--r--g10/keyedit.c4
-rw-r--r--g10/keyring.c4
-rw-r--r--g13/server.c21
-rw-r--r--g13/sh-cmd.c28
-rw-r--r--kbx/keybox-search.c16
-rw-r--r--scd/app-openpgp.c11
-rw-r--r--scd/command.c4
-rw-r--r--scd/scdaemon.c39
-rw-r--r--sm/export.c3
-rw-r--r--sm/server.c21
-rw-r--r--tests/asschk.c2
-rw-r--r--tools/gpg-wks-client.c167
-rw-r--r--tools/gpg-wks-server.c246
-rw-r--r--tools/gpg-wks.h6
-rw-r--r--tools/wks-util.c158
30 files changed, 654 insertions, 336 deletions
diff --git a/AUTHORS b/AUTHORS
index fd215d26f..0dabbc127 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,9 +15,9 @@ copyrightable year that would otherwise be listed individually.
List of Copyright holders
=========================
- Copyright (C) 1997-2017 Werner Koch
- Copyright (C) 1994-2017 Free Software Foundation, Inc.
- Copyright (C) 2003-2013,2015-2017 g10 Code GmbH
+ Copyright (C) 1997-2018 Werner Koch
+ Copyright (C) 1994-2018 Free Software Foundation, Inc.
+ Copyright (C) 2003-2013,2015-2018 g10 Code GmbH
Copyright (C) 2002 Klarälvdalens Datakonsult AB
Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper <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 f4b549c4a..39ccc4d5e 100644
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
=========================
Version 2.2
- Copyright 1997-2017 Werner Koch
- Copyright 1998-2017 Free Software Foundation, Inc.
+ Copyright 1997-2018 Werner Koch
+ Copyright 1998-2018 Free Software Foundation, Inc.
* INTRODUCTION
diff --git a/agent/command.c b/agent/command.c
index 7c7e8a4bc..e2486a556 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -293,50 +293,19 @@ parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf)
/* Write an Assuan status line. KEYWORD is the first item on the
- status line. The following arguments are all separated by a space
- in the output. The last argument must be a NULL. Linefeeds and
- carriage returns characters (which are not allowed in an Assuan
- status line) are silently quoted in C-style. */
+ * status line. The following arguments are all separated by a space
+ * in the output. The last argument must be a NULL. Linefeeds and
+ * carriage returns characters (which are not allowed in an Assuan
+ * status line) are silently quoted in C-style. */
gpg_error_t
agent_write_status (ctrl_t ctrl, const char *keyword, ...)
{
- gpg_error_t err = 0;
+ gpg_error_t err;
va_list arg_ptr;
- const char *text;
assuan_context_t ctx = ctrl->server_local->assuan_ctx;
- char buf[950], *p;
- size_t n;
va_start (arg_ptr, keyword);
-
- p = buf;
- n = 0;
- while ( (text = va_arg (arg_ptr, const char *)) )
- {
- if (n)
- {
- *p++ = ' ';
- n++;
- }
- for ( ; *text && n < DIM (buf)-3; n++, text++)
- {
- if (*text == '\n')
- {
- *p++ = '\\';
- *p++ = 'n';
- }
- else if (*text == '\r')
- {
- *p++ = '\\';
- *p++ = 'r';
- }
- else
- *p++ = *text;
- }
- }
- *p = 0;
- err = assuan_write_status (ctx, keyword, buf);
-
+ err = vprint_assuan_status_strings (ctx, keyword, arg_ptr);
va_end (arg_ptr);
return err;
}
@@ -1062,7 +1031,8 @@ cmd_readkey (assuan_context_t ctx, char *line)
rc = gpg_error_from_syserror ();
else
{
- gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen);
+ pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON,
+ pkbuf, pkbuflen);
rc = assuan_send_data (ctx, pkbuf, pkbuflen);
}
}
diff --git a/build-aux/speedo.mk b/build-aux/speedo.mk
index 2b3b72b86..320d4403d 100644
--- a/build-aux/speedo.mk
+++ b/build-aux/speedo.mk
@@ -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
diff --git a/common/argparse.c b/common/argparse.c
index f5e4ceb9d..90d0ff7f3 100644
--- a/common/argparse.c
+++ b/common/argparse.c
@@ -71,7 +71,7 @@
#else /* Used by GnuPG */
# define ARGPARSE_GPL_VERSION 3
-# define ARGPARSE_CRIGHT_STR "Copyright (C) 2017 Free Software Foundation, Inc."
+# define ARGPARSE_CRIGHT_STR "Copyright (C) 2018 Free Software Foundation, Inc."
#endif /*GNUPG_MAJOR_VERSION*/
diff --git a/common/asshelp.h b/common/asshelp.h
index f169d8774..bf1bd1705 100644
--- a/common/asshelp.h
+++ b/common/asshelp.h
@@ -93,5 +93,12 @@ gpg_error_t vprint_assuan_status (assuan_context_t ctx,
const char *format,
va_list arg_ptr) GPGRT_ATTR_PRINTF(3,0);
+gpg_error_t vprint_assuan_status_strings (assuan_context_t ctx,
+ const char *keyword,
+ va_list arg_ptr);
+gpg_error_t print_assuan_status_strings (assuan_context_t ctx,
+ const char *keyword,
+ ...) GPGRT_ATTR_SENTINEL(1);
+
#endif /*GNUPG_COMMON_ASSHELP_H*/
diff --git a/common/asshelp2.c b/common/asshelp2.c
index f85c1e67e..0a7c4549d 100644
--- a/common/asshelp2.c
+++ b/common/asshelp2.c
@@ -71,3 +71,66 @@ print_assuan_status (assuan_context_t ctx,
va_end (arg_ptr);
return err;
}
+
+
+/* Helper function to print a list of strings as an assuan status
+ * line. KEYWORD is the first item on the status line. ARG_PTR is a
+ * list of strings which are all separated by a space in the output.
+ * The last argument must be a NULL. Linefeeds and carriage returns
+ * characters (which are not allowed in an Assuan status line) are
+ * silently quoted in C-style. */
+gpg_error_t
+vprint_assuan_status_strings (assuan_context_t ctx,
+ const char *keyword, va_list arg_ptr)
+{
+ gpg_error_t err = 0;
+ const char *text;
+ char buf[950], *p;
+ size_t n;
+
+ p = buf;
+ n = 0;
+ while ((text = va_arg (arg_ptr, const char *)) && n < DIM (buf)-3 )
+ {
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; *text && n < DIM (buf)-3; n++, text++)
+ {
+ if (*text == '\n')
+ {
+ *p++ = '\\';
+ *p++ = 'n';
+ n++;
+ }
+ else if (*text == '\r')
+ {
+ *p++ = '\\';
+ *p++ = 'r';
+ n++;
+ }
+ else
+ *p++ = *text;
+ }
+ }
+ *p = 0;
+ err = assuan_write_status (ctx, keyword, buf);
+
+ return err;
+}
+
+
+/* See vprint_assuan_status_strings. */
+gpg_error_t
+print_assuan_status_strings (assuan_context_t ctx, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ gpg_error_t err;
+
+ va_start (arg_ptr, keyword);
+ err = vprint_assuan_status_strings (ctx, keyword, arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}
diff --git a/common/w32info-rc.h.in b/common/w32info-rc.h.in
index 4e46b978a..2ff686321 100644
--- a/common/w32info-rc.h.in
+++ b/common/w32info-rc.h.in
@@ -29,4 +29,4 @@ built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0"
#define W32INFO_PRODUCTVERSION "@VERSION@\0"
#define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \
-2017 Free Software Foundation, Inc.\0"
+2018 Free Software Foundation, Inc.\0"
diff --git a/configure.ac b/configure.ac
index 62687664a..f68065800 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac - for GnuPG 2.1
-# Copyright (C) 1998-2017 Free Software Foundation, Inc.
-# Copyright (C) 1998-2017 Werner Koch
+# Copyright (C) 1998-2018 Free Software Foundation, Inc.
+# Copyright (C) 1998-2018 Werner Koch
#
# This file is part of GnuPG.
#
@@ -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}"
diff --git a/dirmngr/server.c b/dirmngr/server.c
index 3d0768b2a..60d980211 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -2134,8 +2134,13 @@ cmd_keyserver (assuan_context_t ctx, char *line)
if (resolve_flag)
{
err = ensure_keyserver (ctrl);
- if (!err)
- err = ks_action_resolve (ctrl, ctrl->server_local->keyservers);
+ if (err)
+ {
+ assuan_set_error (ctx, err,
+ "Bad keyserver configuration in dirmngr.conf");
+ goto leave;
+ }
+ err = ks_action_resolve (ctrl, ctrl->server_local->keyservers);
if (err)
goto leave;
}
@@ -2829,30 +2834,13 @@ dirmngr_status (ctrl_t ctrl, const char *keyword, ...)
{
gpg_error_t err = 0;
va_list arg_ptr;
- const char *text;
assuan_context_t ctx;
va_start (arg_ptr, keyword);
if (ctrl->server_local && (ctx = ctrl->server_local->assuan_ctx))
{
- char buf[950], *p;
- size_t n;
-
- p = buf;
- n = 0;
- while ( (text = va_arg (arg_ptr, const char *)) )
- {
- if (n)
- {
- *p++ = ' ';
- n++;
- }
- for ( ; *text && n < DIM (buf)-2; n++)
- *p++ = *text++;
- }
- *p = 0;
- err = assuan_write_status (ctx, keyword, buf);
+ err = vprint_assuan_status_strings (ctx, keyword, arg_ptr);
}
va_end (arg_ptr);
diff --git a/doc/examples/vsnfd.prf b/doc/examples/vsnfd.prf
index e8732de00..1dc21e0a7 100644
--- a/doc/examples/vsnfd.prf
+++ b/doc/examples/vsnfd.prf
@@ -6,6 +6,7 @@ default-new-key-algo rsa3072/cert,sign+rsa3072/encr
[gpgsm]
enable-crl-checks
+compliance de-vs
[gpg-agent]
enable-extended-key-format
diff --git a/doc/gpg.texi b/doc/gpg.texi
index 3a2c0ff7f..8fea489f0 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -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
diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
index b187a54d5..37a535366 100644
--- a/doc/gpgsm.texi
+++ b/doc/gpgsm.texi
@@ -257,10 +257,10 @@ fingerprints or keygrips.
@item --export-secret-key-p12 @var{key-id}
@opindex export-secret-key-p12
Export the private key and the certificate identified by @var{key-id} in
-a PKCS#12 format. When used with the @code{--armor} option a few
+using the PKCS#12 format. When used with the @code{--armor} option a few
informational lines are prepended to the output. Note, that the PKCS#12
-format is not very secure and this command is only provided if there is
-no other way to exchange the private key. (@xref{option --p12-charset}.)
+format is not very secure and proper transport security should be used
+to convey the exported key. (@xref{option --p12-charset}.)
@item --export-secret-key-p8 @var{key-id}
@itemx --export-secret-key-raw @var{key-id}
diff --git a/doc/wks.texi b/doc/wks.texi
index 131a4c2c2..4508ae2a1 100644
--- a/doc/wks.texi
+++ b/doc/wks.texi
@@ -181,6 +181,7 @@ Display a brief help page and exit.
.RI [ options ]
.B \-\-install-key
.I file
+.I user-id
.br
.B gpg-wks-server
.RI [ options ]
@@ -221,14 +222,19 @@ the process returns failure; to suppress the diagnostic, use option
@option{-q}. More than one user-id can be given; see also option
@option{with-file}.
+The command @option{--install-key} manually installs a key into the
+WKD. The arguments are a file with the keyblock and the user-id to
+install. If the first argument resembles a fingerprint the key is
+taken from the current keyring; to force the use of a file, prefix the
+first argument with "./".
+
The command @option{--remove-key} uninstalls a key from the WKD. The
-process return success in this case; to also print a diagnostic, use
-option @option{-v}. If the key is not installed a diagnostics is
+process returns success in this case; to also print a diagnostic, use
+option @option{-v}. If the key is not installed a diagnostic is
printed and the process returns failure; to suppress the diagnostic,
use option @option{-q}.
-The commands @option{--install-key} and @option{--revoke-key} are not
-yet functional.
+The command @option{--revoke-key} is not yet functional.
@mansect options
@@ -326,7 +332,7 @@ the submission address:
@example
$ gpg --batch --passphrase '' --quick-gen-key key-submission@@example.net
- $ gpg --with-wkd-hash -K key-submission@@example.net
+ $ gpg -K key-submission@@example.net
@end example
The output of the last command looks similar to this:
@@ -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"
diff --git a/g10/keydb.c b/g10/keydb.c
index 58a14a83d..03fadfd54 100644
--- a/g10/keydb.c
+++ b/g10/keydb.c
@@ -1269,10 +1269,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
break; /* Allowed per RFC. */
default:
- /* Note that can't allow ring trust packets here and some of
- the other GPG specific packets don't make sense either. */
- log_error ("skipped packet of type %d in keybox\n",
- (int)pkt->pkttype);
+ log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype);
free_packet(pkt, &parsectx);
init_packet(pkt);
continue;
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 3ae96a3b2..2c33a29dd 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1664,11 +1664,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
if (opt.only_sign_text_ids)
result = cpr_get_answer_is_yes
("keyedit.sign_all.okay",
- _("Really sign all user IDs? (y/N) "));
+ _("Really sign all text user IDs? (y/N) "));
else
result = cpr_get_answer_is_yes
("keyedit.sign_all.okay",
- _("Really sign all text user IDs? (y/N) "));
+ _("Really sign all user IDs? (y/N) "));
if (! result)
{
diff --git a/g10/keyring.c b/g10/keyring.c
index 50f1b824c..25ef50747 100644
--- a/g10/keyring.c
+++ b/g10/keyring.c
@@ -459,8 +459,8 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
break; /* Allowed by us. */
default:
- log_error ("skipped packet of type %d in keyring\n",
- (int)pkt->pkttype);
+ log_info ("skipped packet of type %d in keyring\n",
+ (int)pkt->pkttype);
free_packet(pkt, &parsectx);
init_packet(pkt);
continue;
diff --git a/g13/server.c b/g13/server.c
index bbe42d4f6..defde6c02 100644
--- a/g13/server.c
+++ b/g13/server.c
@@ -34,6 +34,7 @@
#include "mount.h"
#include "suspend.h"
#include "../common/server-help.h"
+#include "../common/asshelp.h"
#include "../common/call-gpg.h"
@@ -737,24 +738,8 @@ g13_status (ctrl_t ctrl, int no, ...)
}
else
{
- assuan_context_t ctx = ctrl->server_local->assuan_ctx;
- char buf[950], *p;
- size_t n;
-
- p = buf;
- n = 0;
- while ( (text = va_arg (arg_ptr, const char *)) )
- {
- if (n)
- {
- *p++ = ' ';
- n++;
- }
- for ( ; *text && n < DIM (buf)-2; n++)
- *p++ = *text++;
- }
- *p = 0;
- err = assuan_write_status (ctx, get_status_string (no), buf);
+ err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
+ get_status_string (no), arg_ptr);
}
va_end (arg_ptr);
diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c
index b57369d9c..791e3b7f4 100644
--- a/g13/sh-cmd.c
+++ b/g13/sh-cmd.c
@@ -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;
}
diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c
index a5fc7fa9d..e309cce98 100644
--- a/kbx/keybox-search.c
+++ b/kbx/keybox-search.c
@@ -247,7 +247,7 @@ blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr)
if (keyinfolen < 28)
return 0; /* invalid blob */
pos = 20;
- if (pos + keyinfolen*nkeys > length)
+ if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length)
return 0; /* out of bounds */
for (idx=0; idx < nkeys; idx++)
@@ -279,7 +279,7 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr,
if (keyinfolen < 28)
return 0; /* invalid blob */
pos = 20;
- if (pos + keyinfolen*nkeys > length)
+ if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length)
return 0; /* out of bounds */
for (idx=0; idx < nkeys; idx++)
@@ -313,7 +313,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
if (keyinfolen < 28)
return 0; /* invalid blob */
pos = 20 + keyinfolen*nkeys;
- if (pos+2 > length)
+ if ((uint64_t)pos+2 > (uint64_t)length)
return 0; /* out of bounds */
/*serial*/
@@ -340,7 +340,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
mypos += idx*uidinfolen;
off = get32 (buffer+mypos);
len = get32 (buffer+mypos+4);
- if (off+len > length)
+ if ((uint64_t)off+(uint64_t)len > (uint64_t)length)
return 0; /* error: better stop here out of bounds */
if (len < 1)
continue; /* empty name */
@@ -439,7 +439,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
mypos += idx*uidinfolen;
off = get32 (buffer+mypos);
len = get32 (buffer+mypos+4);
- if (off+len > length)
+ if ((uint64_t)off+(uint64_t)len > (uint64_t)length)
return 0; /* error: better stop here - out of bounds */
if (x509)
{
@@ -522,7 +522,7 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
return 0; /* Too short. */
cert_off = get32 (buffer+8);
cert_len = get32 (buffer+12);
- if (cert_off+cert_len > length)
+ if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length)
return 0; /* Too short. */
rc = ksba_reader_new (&reader);
@@ -1097,7 +1097,7 @@ keybox_get_keyblock (KEYBOX_HANDLE hd, iobuf_t *r_iobuf,
return gpg_error (GPG_ERR_TOO_SHORT);
image_off = get32 (buffer+8);
image_len = get32 (buffer+12);
- if (image_off+image_len > length)
+ if ((uint64_t)image_off+(uint64_t)image_len > (uint64_t)length)
return gpg_error (GPG_ERR_TOO_SHORT);
err = _keybox_get_flag_location (buffer, length, KEYBOX_FLAG_SIG_INFO,
@@ -1139,7 +1139,7 @@ keybox_get_cert (KEYBOX_HANDLE hd, ksba_cert_t *r_cert)
return gpg_error (GPG_ERR_TOO_SHORT);
cert_off = get32 (buffer+8);
cert_len = get32 (buffer+12);
- if (cert_off+cert_len > length)
+ if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length)
return gpg_error (GPG_ERR_TOO_SHORT);
rc = ksba_reader_new (&reader);
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index fb869b2bf..54f04c612 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -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;
diff --git a/scd/command.c b/scd/command.c
index 6bcbce4fc..701151894 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -1848,7 +1848,8 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
p = buf;
n = 0;
- while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+ while ( (value = va_arg (arg_ptr, const unsigned char *))
+ && n < DIM (buf)-2 )
{
valuelen = va_arg (arg_ptr, size_t);
if (!valuelen)
@@ -1865,6 +1866,7 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
{
sprintf (p, "%%%02X", *value);
p += 3;
+ n += 2;
}
else if (*value == ' ')
*p++ = '+';
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 3ad265781..cebeea9d3 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -236,6 +236,10 @@ static HANDLE the_event;
/* PID to notify update of usb devices. */
static pid_t main_thread_pid;
#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+/* FD to notify changes. */
+static int notify_fd;
+#endif
static char *create_socket_name (char *standard_name);
static gnupg_fd_t create_server_socket (const char *name,
@@ -1210,6 +1214,8 @@ scd_kick_the_loop (void)
if (ret == 0)
log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
w32_strerror (-1));
+#elif defined(HAVE_PSELECT_NO_EINTR)
+ write (notify_fd, "", 1);
#else
ret = kill (main_thread_pid, SIGCONT);
if (ret < 0)
@@ -1241,6 +1247,17 @@ handle_connections (int listen_fd)
#else
int signo;
#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ int pipe_fd[2];
+
+ ret = gnupg_create_pipe (pipe_fd);
+ if (ret)
+ {
+ log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
+ return;
+ }
+ notify_fd = pipe_fd[1];
+#endif
ret = npth_attr_init(&tattr);
if (ret)
@@ -1298,6 +1315,7 @@ handle_connections (int listen_fd)
for (;;)
{
int periodical_check;
+ int max_fd = nfd;
if (shutdown_pending)
{
@@ -1326,8 +1344,14 @@ handle_connections (int listen_fd)
thus a simple assignment is fine to copy the entire set. */
read_fdset = fdset;
+#ifdef HAVE_PSELECT_NO_EINTR
+ FD_SET (pipe_fd[0], &read_fdset);
+ if (max_fd < pipe_fd[0])
+ max_fd = pipe_fd[0];
+#endif
+
#ifndef HAVE_W32_SYSTEM
- ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t,
+ ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
npth_sigev_sigmask ());
saved_errno = errno;
@@ -1353,6 +1377,15 @@ handle_connections (int listen_fd)
/* Timeout. Will be handled when calculating the next timeout. */
continue;
+#ifdef HAVE_PSELECT_NO_EINTR
+ if (FD_ISSET (pipe_fd[0], &read_fdset))
+ {
+ char buf[256];
+
+ read (pipe_fd[0], buf, sizeof buf);
+ }
+#endif
+
if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
{
ctrl_t ctrl;
@@ -1394,6 +1427,10 @@ handle_connections (int listen_fd)
if (the_event != INVALID_HANDLE_VALUE)
CloseHandle (the_event);
#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ close (pipe_fd[0]);
+ close (pipe_fd[1]);
+#endif
cleanup ();
log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
npth_attr_destroy (&tattr);
diff --git a/sm/export.c b/sm/export.c
index 29a5ac32e..7bea9ccc5 100644
--- a/sm/export.c
+++ b/sm/export.c
@@ -479,6 +479,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode)
leave:
gnupg_ksba_destroy_writer (b64writer);
ksba_cert_release (cert);
+ xfree (keygrip);
xfree (desc);
keydb_release (hd);
}
@@ -603,7 +604,7 @@ sexp_to_kparms (gcry_sexp_t sexp)
array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */
gcry_mpi_sub_ui (array[6], array[4], 1);
- gcry_mpi_mod (array[6], array[3], array[6]);
+ gcry_mpi_mod (array[6], array[2], array[6]);
return array;
}
diff --git a/sm/server.c b/sm/server.c
index 568e51b17..721f3faf0 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -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);
diff --git a/tests/asschk.c b/tests/asschk.c
index 2595c0a99..65828e5b2 100644
--- a/tests/asschk.c
+++ b/tests/asschk.c
@@ -116,7 +116,7 @@
#endif
#if __STDC_VERSION__ < 199901L
-# if __GNUC__ >= 2
+# if __GNUC__ >= 2 && !defined (__func__)
# define __func__ __FUNCTION__
# else
/* Let's try our luck here. Some systems may provide __func__ without
diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c
index 73a8a1f43..73945ff30 100644
--- a/tools/gpg-wks-client.c
+++ b/tools/gpg-wks-client.c
@@ -325,119 +325,6 @@ main (int argc, char **argv)
-struct get_key_status_parm_s
-{
- const char *fpr;
- int found;
- int count;
-};
-
-static void
-get_key_status_cb (void *opaque, const char *keyword, char *args)
-{
- struct get_key_status_parm_s *parm = opaque;
-
- /*log_debug ("%s: %s\n", keyword, args);*/
- if (!strcmp (keyword, "EXPORTED"))
- {
- parm->count++;
- if (!ascii_strcasecmp (args, parm->fpr))
- parm->found = 1;
- }
-}
-
-
-/* Get a key by fingerprint from gpg's keyring and make sure that the
- * mail address ADDRSPEC is included in the key. If EXACT is set the
- * returned user id must match Addrspec exactly and not just in the
- * addr-spec (mailbox) part. The key is returned as a new memory
- * stream at R_KEY. */
-static gpg_error_t
-get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
- int exact)
-{
- gpg_error_t err;
- ccparray_t ccp;
- const char **argv = NULL;
- estream_t key = NULL;
- struct get_key_status_parm_s parm;
- char *filterexp = NULL;
-
- memset (&parm, 0, sizeof parm);
-
- *r_key = NULL;
-
- key = es_fopenmem (0, "w+b");
- if (!key)
- {
- err = gpg_error_from_syserror ();
- log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- /* Prefix the key with the MIME content type. */
- es_fputs ("Content-Type: application/pgp-keys\n"
- "\n", key);
-
- filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
- if (!filterexp)
- {
- err = gpg_error_from_syserror ();
- log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- ccparray_init (&ccp, 0);
-
- ccparray_put (&ccp, "--no-options");
- if (!opt.verbose)
- ccparray_put (&ccp, "--quiet");
- else if (opt.verbose > 1)
- ccparray_put (&ccp, "--verbose");
- ccparray_put (&ccp, "--batch");
- ccparray_put (&ccp, "--status-fd=2");
- ccparray_put (&ccp, "--always-trust");
- ccparray_put (&ccp, "--armor");
- ccparray_put (&ccp, "--export-options=export-minimal");
- ccparray_put (&ccp, "--export-filter");
- ccparray_put (&ccp, filterexp);
- ccparray_put (&ccp, "--export");
- ccparray_put (&ccp, "--");
- ccparray_put (&ccp, fingerprint);
-
- ccparray_put (&ccp, NULL);
- argv = ccparray_get (&ccp, NULL);
- if (!argv)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- parm.fpr = fingerprint;
- err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
- NULL, key,
- get_key_status_cb, &parm);
- if (!err && parm.count > 1)
- err = gpg_error (GPG_ERR_TOO_MANY);
- else if (!err && !parm.found)
- err = gpg_error (GPG_ERR_NOT_FOUND);
- if (err)
- {
- log_error ("export failed: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- es_rewind (key);
- *r_key = key;
- key = NULL;
-
- leave:
- es_fclose (key);
- xfree (argv);
- xfree (filterexp);
- return err;
-}
-
-
/* Add the user id UID to the key identified by FINGERPRINT. */
static gpg_error_t
add_user_id (const char *fingerprint, const char *uid)
@@ -767,7 +654,7 @@ command_send (const char *fingerprint, const char *userid)
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
- err = get_key (&key, fingerprint, addrspec, 0);
+ err = wks_get_key (&key, fingerprint, addrspec, 0);
if (err)
goto leave;
@@ -782,27 +669,19 @@ command_send (const char *fingerprint, const char *userid)
err = 0;
}
else
- err = wkd_get_submission_address (addrspec, &submission_to);
- if (err)
- {
- log_error (_("error looking up submission address for domain '%s': %s\n"),
- domain, gpg_strerror (err));
- if (gpg_err_code (err) == GPG_ERR_NO_DATA)
- log_error (_("this domain probably doesn't support WKS.\n"));
- goto leave;
- }
- log_info ("submitting request to '%s'\n", submission_to);
-
- /* Get the policy flags. */
- if (!fake_submission_addr)
{
+ /* We first try to get the submission address from the policy
+ * file (this is the new method). If both are available we
+ * check that they match and print a warning if not. In the
+ * latter case we keep on using the one from the
+ * submission-address file. */
estream_t mbuf;
err = wkd_get_policy_flags (addrspec, &mbuf);
if (err && gpg_err_code (err) != GPG_ERR_NO_DATA)
{
log_error ("error reading policy flags for '%s': %s\n",
- submission_to, gpg_strerror (err));
+ domain, gpg_strerror (err));
goto leave;
}
if (mbuf)
@@ -812,8 +691,35 @@ command_send (const char *fingerprint, const char *userid)
if (err)
goto leave;
}
+
+ err = wkd_get_submission_address (addrspec, &submission_to);
+ if (err && !policy.submission_address)
+ {
+ log_error (_("error looking up submission address for domain '%s'"
+ ": %s\n"), domain, gpg_strerror (err));
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ log_error (_("this domain probably doesn't support WKS.\n"));
+ goto leave;
+ }
+
+ if (submission_to && policy.submission_address
+ && ascii_strcasecmp (submission_to, policy.submission_address))
+ log_info ("Warning: different submission addresses (sa=%s, po=%s)\n",
+ submission_to, policy.submission_address);
+
+ if (!submission_to)
+ {
+ submission_to = xtrystrdup (policy.submission_address);
+ if (!submission_to)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
}
+ log_info ("submitting request to '%s'\n", submission_to);
+
if (policy.auth_submit)
log_info ("no confirmation required for '%s'\n", addrspec);
@@ -853,7 +759,7 @@ command_send (const char *fingerprint, const char *userid)
estream_t newkey;
es_rewind (key);
- err = wks_filter_uid (&newkey, key, thisuid->uid);
+ err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
if (err)
{
log_error ("error filtering key: %s\n", gpg_strerror (err));
@@ -878,7 +784,7 @@ command_send (const char *fingerprint, const char *userid)
* the key again. */
es_fclose (key);
key = NULL;
- err = get_key (&key, fingerprint, addrspec, 1);
+ err = wks_get_key (&key, fingerprint, addrspec, 1);
if (err)
goto leave;
}
@@ -1002,6 +908,7 @@ command_send (const char *fingerprint, const char *userid)
free_uidinfo_list (uidlist);
es_fclose (keyenc);
es_fclose (key);
+ wks_free_policy (&policy);
xfree (addrspec);
return err;
}
diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c
index 0b1d64261..a5881557f 100644
--- a/tools/gpg-wks-server.c
+++ b/tools/gpg-wks-server.c
@@ -1,5 +1,5 @@
/* gpg-wks-server.c - A server for the Web Key Service protocols.
- * Copyright (C) 2016 Werner Koch
+ * Copyright (C) 2016, 2018 Werner Koch
* Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GnuPG.
@@ -20,7 +20,7 @@
/* The Web Key Service I-D defines an update protocol to store a
* public key in the Web Key Directory. The current specification is
- * draft-koch-openpgp-webkey-service-01.txt.
+ * draft-koch-openpgp-webkey-service-05.txt.
*/
#include <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)
diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h
index ece7add5f..1b91b6504 100644
--- a/tools/gpg-wks.h
+++ b/tools/gpg-wks.h
@@ -60,6 +60,7 @@ struct
/* The parsed policy flags. */
struct policy_flags_s
{
+ char *submission_address;
unsigned int mailbox_only : 1;
unsigned int dane_only : 1;
unsigned int auth_submit : 1;
@@ -85,13 +86,16 @@ typedef struct uidinfo_list_s *uidinfo_list_t;
void wks_set_status_fd (int fd);
void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
void free_uidinfo_list (uidinfo_list_t list);
+gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint,
+ const char *addrspec, int exact);
gpg_error_t wks_list_key (estream_t key, char **r_fpr,
uidinfo_list_t *r_mboxes);
gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key,
- const char *uid);
+ const char *uid, int binary);
gpg_error_t wks_send_mime (mime_maker_t mime);
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
int ignore_unknown);
+void wks_free_policy (policy_flags_t policy);
/*-- wks-receive.c --*/
diff --git a/tools/wks-util.c b/tools/wks-util.c
index 889ca36dc..3fd824c1a 100644
--- a/tools/wks-util.c
+++ b/tools/wks-util.c
@@ -133,6 +133,120 @@ free_uidinfo_list (uidinfo_list_t list)
+struct get_key_status_parm_s
+{
+ const char *fpr;
+ int found;
+ int count;
+};
+
+
+static void
+get_key_status_cb (void *opaque, const char *keyword, char *args)
+{
+ struct get_key_status_parm_s *parm = opaque;
+
+ /*log_debug ("%s: %s\n", keyword, args);*/
+ if (!strcmp (keyword, "EXPORTED"))
+ {
+ parm->count++;
+ if (!ascii_strcasecmp (args, parm->fpr))
+ parm->found = 1;
+ }
+}
+
+/* Get a key by fingerprint from gpg's keyring and make sure that the
+ * mail address ADDRSPEC is included in the key. If EXACT is set the
+ * returned user id must match Addrspec exactly and not just in the
+ * addr-spec (mailbox) part. The key is returned as a new memory
+ * stream at R_KEY. */
+gpg_error_t
+wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
+ int exact)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv = NULL;
+ estream_t key = NULL;
+ struct get_key_status_parm_s parm;
+ char *filterexp = NULL;
+
+ memset (&parm, 0, sizeof parm);
+
+ *r_key = NULL;
+
+ key = es_fopenmem (0, "w+b");
+ if (!key)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Prefix the key with the MIME content type. */
+ es_fputs ("Content-Type: application/pgp-keys\n"
+ "\n", key);
+
+ filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
+ if (!filterexp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ ccparray_init (&ccp, 0);
+
+ ccparray_put (&ccp, "--no-options");
+ if (!opt.verbose)
+ ccparray_put (&ccp, "--quiet");
+ else if (opt.verbose > 1)
+ ccparray_put (&ccp, "--verbose");
+ ccparray_put (&ccp, "--batch");
+ ccparray_put (&ccp, "--status-fd=2");
+ ccparray_put (&ccp, "--always-trust");
+ ccparray_put (&ccp, "--armor");
+ ccparray_put (&ccp, "--export-options=export-minimal");
+ ccparray_put (&ccp, "--export-filter");
+ ccparray_put (&ccp, filterexp);
+ ccparray_put (&ccp, "--export");
+ ccparray_put (&ccp, "--");
+ ccparray_put (&ccp, fingerprint);
+
+ ccparray_put (&ccp, NULL);
+ argv = ccparray_get (&ccp, NULL);
+ if (!argv)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ parm.fpr = fingerprint;
+ err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
+ NULL, key,
+ get_key_status_cb, &parm);
+ if (!err && parm.count > 1)
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ else if (!err && !parm.found)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (err)
+ {
+ log_error ("export failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ es_rewind (key);
+ *r_key = key;
+ key = NULL;
+
+ leave:
+ es_fclose (key);
+ xfree (argv);
+ xfree (filterexp);
+ return err;
+}
+
+
+
/* Helper for wks_list_key and wks_filter_uid. */
static void
key_status_cb (void *opaque, const char *keyword, char *args)
@@ -317,10 +431,13 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
/* Run gpg as a filter on KEY and write the output to a new stream
- * stored at R_NEWKEY. The new key will containn only the user id
- * UID. Returns 0 on success. Only one key is expected in KEY. */
+ * stored at R_NEWKEY. The new key will contain only the user id UID.
+ * Returns 0 on success. Only one key is expected in KEY. If BINARY
+ * is set the resulting key is returned as a binary (non-armored)
+ * keyblock. */
gpg_error_t
-wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
+wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid,
+ int binary)
{
gpg_error_t err;
ccparray_t ccp;
@@ -340,8 +457,9 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
}
/* Prefix the key with the MIME content type. */
- es_fputs ("Content-Type: application/pgp-keys\n"
- "\n", newkey);
+ if (!binary)
+ es_fputs ("Content-Type: application/pgp-keys\n"
+ "\n", newkey);
filterexp = es_bsprintf ("keep-uid=uid=%s", uid);
if (!filterexp)
@@ -361,7 +479,8 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
ccparray_put (&ccp, "--batch");
ccparray_put (&ccp, "--status-fd=2");
ccparray_put (&ccp, "--always-trust");
- ccparray_put (&ccp, "--armor");
+ if (!binary)
+ ccparray_put (&ccp, "--armor");
ccparray_put (&ccp, "--import-options=import-export");
ccparray_put (&ccp, "--import-filter");
ccparray_put (&ccp, filterexp);
@@ -443,6 +562,7 @@ gpg_error_t
wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
{
enum tokens {
+ TOK_SUBMISSION_ADDRESS,
TOK_MAILBOX_ONLY,
TOK_DANE_ONLY,
TOK_AUTH_SUBMIT,
@@ -453,6 +573,7 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
const char *name;
enum tokens token;
} keywords[] = {
+ { "submission-address", TOK_SUBMISSION_ADDRESS },
{ "mailbox-only", TOK_MAILBOX_ONLY },
{ "dane-only", TOK_DANE_ONLY },
{ "auth-submit", TOK_AUTH_SUBMIT },
@@ -519,6 +640,20 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
switch (keywords[i].token)
{
+ case TOK_SUBMISSION_ADDRESS:
+ if (!value || !*value)
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+ xfree (flags->submission_address);
+ flags->submission_address = xtrystrdup (value);
+ if (!flags->submission_address)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ break;
case TOK_MAILBOX_ONLY: flags->mailbox_only = 1; break;
case TOK_DANE_ONLY: flags->dane_only = 1; break;
case TOK_AUTH_SUBMIT: flags->auth_submit = 1; break;
@@ -553,3 +688,14 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
return err;
}
+
+
+void
+wks_free_policy (policy_flags_t policy)
+{
+ if (policy)
+ {
+ xfree (policy->submission_address);
+ memset (policy, 0, sizeof *policy);
+ }
+}