Merge branch 'STABLE-BRANCH-2-2'

This commit is contained in:
Werner Koch 2018-02-22 16:19:56 +01:00
commit 20539ea5ca
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
30 changed files with 654 additions and 336 deletions

10
AUTHORS
View File

@ -15,9 +15,9 @@ copyrightable year that would otherwise be listed individually.
List of Copyright holders List of Copyright holders
========================= =========================
Copyright (C) 1997-2017 Werner Koch Copyright (C) 1997-2018 Werner Koch
Copyright (C) 1994-2017 Free Software Foundation, Inc. Copyright (C) 1994-2018 Free Software Foundation, Inc.
Copyright (C) 2003-2013,2015-2017 g10 Code GmbH Copyright (C) 2003-2013,2015-2018 g10 Code GmbH
Copyright (C) 2002 Klarälvdalens Datakonsult AB Copyright (C) 2002 Klarälvdalens Datakonsult AB
Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper <drepper@gnu.ai.mit.edu> Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper <drepper@gnu.ai.mit.edu>
Copyright (C) 1994 X Consortium Copyright (C) 1994 X Consortium
@ -264,8 +264,8 @@ domain.
========= =========
Copyright 1998-2017 Free Software Foundation, Inc. Copyright 1998-2018 Free Software Foundation, Inc.
Copyright 1997-2017 Werner Koch Copyright 1997-2018 Werner Koch
This file is free software; as a special exception the author gives This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without unlimited permission to copy and/or distribute it, with or without

4
README
View File

@ -2,8 +2,8 @@
========================= =========================
Version 2.2 Version 2.2
Copyright 1997-2017 Werner Koch Copyright 1997-2018 Werner Koch
Copyright 1998-2017 Free Software Foundation, Inc. Copyright 1998-2018 Free Software Foundation, Inc.
* INTRODUCTION * INTRODUCTION

View 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 /* Write an Assuan status line. KEYWORD is the first item on the
status line. The following arguments are all separated by a space * status line. The following arguments are all separated by a space
in the output. The last argument must be a NULL. Linefeeds and * in the output. The last argument must be a NULL. Linefeeds and
carriage returns characters (which are not allowed in an Assuan * carriage returns characters (which are not allowed in an Assuan
status line) are silently quoted in C-style. */ * status line) are silently quoted in C-style. */
gpg_error_t gpg_error_t
agent_write_status (ctrl_t ctrl, const char *keyword, ...) agent_write_status (ctrl_t ctrl, const char *keyword, ...)
{ {
gpg_error_t err = 0; gpg_error_t err;
va_list arg_ptr; va_list arg_ptr;
const char *text;
assuan_context_t ctx = ctrl->server_local->assuan_ctx; assuan_context_t ctx = ctrl->server_local->assuan_ctx;
char buf[950], *p;
size_t n;
va_start (arg_ptr, keyword); va_start (arg_ptr, keyword);
err = vprint_assuan_status_strings (ctx, keyword, arg_ptr);
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);
va_end (arg_ptr); va_end (arg_ptr);
return err; return err;
} }
@ -1062,7 +1031,8 @@ cmd_readkey (assuan_context_t ctx, char *line)
rc = gpg_error_from_syserror (); rc = gpg_error_from_syserror ();
else 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); rc = assuan_send_data (ctx, pkbuf, pkbuflen);
} }
} }

View File

@ -63,6 +63,7 @@ help:
@echo 'You may append INSTALL_PREFIX=<dir> for native builds.' @echo 'You may append INSTALL_PREFIX=<dir> for native builds.'
@echo 'Prepend TARGET with "git-" to build from GIT repos.' @echo 'Prepend TARGET with "git-" to build from GIT repos.'
@echo 'Prepend TARGET with "this-" to build from the source tarball.' @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 SELFCHECK=0 for a non-released version.'
@echo 'Use CUSTOM_SWDB=1 for an already downloaded swdb.lst.' @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 # Set to 0 to skip the GnuPG version self-check
SELFCHECK=1 SELFCHECK=1
# Set to 1 to build with statically linked libraries.
STATIC=0
# Set to the location of the directory with tarballs of # Set to the location of the directory with tarballs of
# external packages. # external packages.
TARBALLS=$(shell pwd)/../tarballs TARBALLS=$(shell pwd)/../tarballs
@ -208,8 +212,10 @@ speedo_spkgs += \
endif endif
endif endif
ifeq ($(STATIC),0)
speedo_spkgs += \ speedo_spkgs += \
gpgme gpgme
endif
ifeq ($(TARGETOS),w32) ifeq ($(TARGETOS),w32)
ifeq ($(WITH_GUI),1) ifeq ($(WITH_GUI),1)
@ -461,6 +467,8 @@ speedo_pkg_gtk__tar = $(pkg2rep)/gtk+-$(gtk__ver).tar.xz
# Package build options # Package build options
# #
speedo_pkg_npth_configure = --enable-static
speedo_pkg_libgpg_error_configure = --enable-static speedo_pkg_libgpg_error_configure = --enable-static
speedo_pkg_w64_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_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 # For now we build ntbtls only static
speedo_pkg_ntbtls_configure = --enable-static --disable-shared speedo_pkg_ntbtls_configure = --disable-shared
ifeq ($(TARGETOS),w32) ifeq ($(TARGETOS),w32)
speedo_pkg_gnupg_configure = \ speedo_pkg_gnupg_configure = \
--disable-g13 --enable-ntbtls \ --disable-g13 --enable-ntbtls \
--enable-build-timestamp --enable-build-timestamp
else else
speedo_pkg_gnupg_configure = --disable-g13 speedo_pkg_gnupg_configure = --disable-g13 --enable-wks-tools
endif endif
speedo_pkg_gnupg_extracflags = -g speedo_pkg_gnupg_extracflags = -g

View File

@ -71,7 +71,7 @@
#else /* Used by GnuPG */ #else /* Used by GnuPG */
# define ARGPARSE_GPL_VERSION 3 # 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*/ #endif /*GNUPG_MAJOR_VERSION*/

View File

@ -93,5 +93,12 @@ gpg_error_t vprint_assuan_status (assuan_context_t ctx,
const char *format, const char *format,
va_list arg_ptr) GPGRT_ATTR_PRINTF(3,0); 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*/ #endif /*GNUPG_COMMON_ASSHELP_H*/

View File

@ -71,3 +71,66 @@ print_assuan_status (assuan_context_t ctx,
va_end (arg_ptr); va_end (arg_ptr);
return err; 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;
}

View File

@ -29,4 +29,4 @@ built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0"
#define W32INFO_PRODUCTVERSION "@VERSION@\0" #define W32INFO_PRODUCTVERSION "@VERSION@\0"
#define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \ #define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \
2017 Free Software Foundation, Inc.\0" 2018 Free Software Foundation, Inc.\0"

View File

@ -1,6 +1,6 @@
# configure.ac - for GnuPG 2.1 # configure.ac - for GnuPG 2.1
# Copyright (C) 1998-2017 Free Software Foundation, Inc. # Copyright (C) 1998-2018 Free Software Foundation, Inc.
# Copyright (C) 1998-2017 Werner Koch # Copyright (C) 1998-2018 Werner Koch
# #
# This file is part of GnuPG. # This file is part of GnuPG.
# #
@ -650,6 +650,7 @@ have_android_system=no
use_simple_gettext=no use_simple_gettext=no
use_ldapwrapper=yes use_ldapwrapper=yes
mmap_needed=yes mmap_needed=yes
require_pipe_to_unblock_pselect=no
case "${host}" in case "${host}" in
*-mingw32*) *-mingw32*)
# special stuff for Windoze NT # special stuff for Windoze NT
@ -726,10 +727,20 @@ case "${host}" in
AC_DEFINE(_DARWIN_C_SOURCE, 900000L, AC_DEFINE(_DARWIN_C_SOURCE, 900000L,
Expose all libc features (__DARWIN_C_FULL).) Expose all libc features (__DARWIN_C_FULL).)
;; ;;
*-*-netbsd*)
require_pipe_to_unblock_pselect=yes
;;
*) *)
;; ;;
esac 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 if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1, AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems [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 ]) have_libusb=yes ])
AC_MSG_CHECKING([libusb include dir]) AC_MSG_CHECKING([libusb include dir])
usb_incdir_found="no" 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 _libusb_save_cppflags=$CPPFLAGS
if test -n "${_incdir}"; then if test -n "${_incdir}"; then
CPPFLAGS="-I${_incdir} ${CPPFLAGS}" CPPFLAGS="-I${_incdir} ${CPPFLAGS}"

View File

@ -2134,8 +2134,13 @@ cmd_keyserver (assuan_context_t ctx, char *line)
if (resolve_flag) if (resolve_flag)
{ {
err = ensure_keyserver (ctrl); err = ensure_keyserver (ctrl);
if (!err) if (err)
err = ks_action_resolve (ctrl, ctrl->server_local->keyservers); {
assuan_set_error (ctx, err,
"Bad keyserver configuration in dirmngr.conf");
goto leave;
}
err = ks_action_resolve (ctrl, ctrl->server_local->keyservers);
if (err) if (err)
goto leave; goto leave;
} }
@ -2829,30 +2834,13 @@ dirmngr_status (ctrl_t ctrl, const char *keyword, ...)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
va_list arg_ptr; va_list arg_ptr;
const char *text;
assuan_context_t ctx; assuan_context_t ctx;
va_start (arg_ptr, keyword); va_start (arg_ptr, keyword);
if (ctrl->server_local && (ctx = ctrl->server_local->assuan_ctx)) if (ctrl->server_local && (ctx = ctrl->server_local->assuan_ctx))
{ {
char buf[950], *p; err = vprint_assuan_status_strings (ctx, keyword, arg_ptr);
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);
} }
va_end (arg_ptr); va_end (arg_ptr);

View File

@ -6,6 +6,7 @@ default-new-key-algo rsa3072/cert,sign+rsa3072/encr
[gpgsm] [gpgsm]
enable-crl-checks enable-crl-checks
compliance de-vs
[gpg-agent] [gpg-agent]
enable-extended-key-format enable-extended-key-format

View 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 program knows about it; either give both filenames on the command line
or use @samp{-} to specify STDIN. 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 @mansect interoperability
@chapheading INTEROPERABILITY WITH OTHER OPENPGP PROGRAMS @chapheading INTEROPERABILITY WITH OTHER OPENPGP PROGRAMS

View File

@ -257,10 +257,10 @@ fingerprints or keygrips.
@item --export-secret-key-p12 @var{key-id} @item --export-secret-key-p12 @var{key-id}
@opindex export-secret-key-p12 @opindex export-secret-key-p12
Export the private key and the certificate identified by @var{key-id} in 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 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 format is not very secure and proper transport security should be used
no other way to exchange the private key. (@xref{option --p12-charset}.) to convey the exported key. (@xref{option --p12-charset}.)
@item --export-secret-key-p8 @var{key-id} @item --export-secret-key-p8 @var{key-id}
@itemx --export-secret-key-raw @var{key-id} @itemx --export-secret-key-raw @var{key-id}

View File

@ -181,6 +181,7 @@ Display a brief help page and exit.
.RI [ options ] .RI [ options ]
.B \-\-install-key .B \-\-install-key
.I file .I file
.I user-id
.br .br
.B gpg-wks-server .B gpg-wks-server
.RI [ options ] .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{-q}. More than one user-id can be given; see also option
@option{with-file}. @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 The command @option{--remove-key} uninstalls a key from the WKD. The
process return success in this case; to also print a diagnostic, use process returns success in this case; to also print a diagnostic, use
option @option{-v}. If the key is not installed a diagnostics is option @option{-v}. If the key is not installed a diagnostic is
printed and the process returns failure; to suppress the diagnostic, printed and the process returns failure; to suppress the diagnostic,
use option @option{-q}. use option @option{-q}.
The commands @option{--install-key} and @option{--revoke-key} are not The command @option{--revoke-key} is not yet functional.
yet functional.
@mansect options @mansect options
@ -326,7 +332,7 @@ the submission address:
@example @example
$ gpg --batch --passphrase '' --quick-gen-key key-submission@@example.net $ 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 @end example
The output of the last command looks similar to this: 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] ssb rsa3072 2016-08-30 [E]
@end example @end example
Take the hash of the string "key-submission", which is Take the fingerprint from that output and manually publish the key:
"bxzcxpxk8h87z1k7bzk86xn5aj47intu" and manually publish that key:
@example @example
$ gpg --export-options export-minimal --export \ $ gpg-wks-server --install-key C0FCF8642D830C53246211400346653590B3795B \
> -o /var/lib/gnupg/wks/example.net/hu/bxzcxpxk8h87z1k7bzk86xn5aj47intu \ > key-submission@@example.net
> key-submission@@example.new
@end example @end example
Make sure that the created file is world readable.
Finally that submission address needs to be redirected to a script Finally that submission address needs to be redirected to a script
running @command{gpg-wks-server}. The @command{procmail} command can running @command{gpg-wks-server}. The @command{procmail} command can
be used for this: Redirect the submission address to the user "webkey" be used for this: Redirect the submission address to the user "webkey"

View File

@ -1269,10 +1269,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
break; /* Allowed per RFC. */ break; /* Allowed per RFC. */
default: default:
/* Note that can't allow ring trust packets here and some of log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype);
the other GPG specific packets don't make sense either. */
log_error ("skipped packet of type %d in keybox\n",
(int)pkt->pkttype);
free_packet(pkt, &parsectx); free_packet(pkt, &parsectx);
init_packet(pkt); init_packet(pkt);
continue; continue;

View File

@ -1664,11 +1664,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
if (opt.only_sign_text_ids) if (opt.only_sign_text_ids)
result = cpr_get_answer_is_yes result = cpr_get_answer_is_yes
("keyedit.sign_all.okay", ("keyedit.sign_all.okay",
_("Really sign all user IDs? (y/N) ")); _("Really sign all text user IDs? (y/N) "));
else else
result = cpr_get_answer_is_yes result = cpr_get_answer_is_yes
("keyedit.sign_all.okay", ("keyedit.sign_all.okay",
_("Really sign all text user IDs? (y/N) ")); _("Really sign all user IDs? (y/N) "));
if (! result) if (! result)
{ {

View File

@ -459,8 +459,8 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
break; /* Allowed by us. */ break; /* Allowed by us. */
default: default:
log_error ("skipped packet of type %d in keyring\n", log_info ("skipped packet of type %d in keyring\n",
(int)pkt->pkttype); (int)pkt->pkttype);
free_packet(pkt, &parsectx); free_packet(pkt, &parsectx);
init_packet(pkt); init_packet(pkt);
continue; continue;

View File

@ -34,6 +34,7 @@
#include "mount.h" #include "mount.h"
#include "suspend.h" #include "suspend.h"
#include "../common/server-help.h" #include "../common/server-help.h"
#include "../common/asshelp.h"
#include "../common/call-gpg.h" #include "../common/call-gpg.h"
@ -737,24 +738,8 @@ g13_status (ctrl_t ctrl, int no, ...)
} }
else else
{ {
assuan_context_t ctx = ctrl->server_local->assuan_ctx; err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
char buf[950], *p; get_status_string (no), arg_ptr);
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);
} }
va_end (arg_ptr); va_end (arg_ptr);

View File

@ -28,6 +28,7 @@
#include "g13-syshelp.h" #include "g13-syshelp.h"
#include <assuan.h> #include <assuan.h>
#include "../common/i18n.h" #include "../common/i18n.h"
#include "../common/asshelp.h"
#include "keyblob.h" #include "keyblob.h"
@ -904,34 +905,13 @@ sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
gpg_error_t gpg_error_t
g13_status (ctrl_t ctrl, int no, ...) g13_status (ctrl_t ctrl, int no, ...)
{ {
gpg_error_t err = 0; gpg_error_t err;
va_list arg_ptr; va_list arg_ptr;
const char *text;
va_start (arg_ptr, no); va_start (arg_ptr, no);
if (1) err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
{ get_status_string (no), arg_ptr);
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);
}
va_end (arg_ptr); va_end (arg_ptr);
return err; return err;
} }

View File

@ -247,7 +247,7 @@ blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr)
if (keyinfolen < 28) if (keyinfolen < 28)
return 0; /* invalid blob */ return 0; /* invalid blob */
pos = 20; pos = 20;
if (pos + keyinfolen*nkeys > length) if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length)
return 0; /* out of bounds */ return 0; /* out of bounds */
for (idx=0; idx < nkeys; idx++) for (idx=0; idx < nkeys; idx++)
@ -279,7 +279,7 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr,
if (keyinfolen < 28) if (keyinfolen < 28)
return 0; /* invalid blob */ return 0; /* invalid blob */
pos = 20; pos = 20;
if (pos + keyinfolen*nkeys > length) if (pos + (uint64_t)keyinfolen*nkeys > (uint64_t)length)
return 0; /* out of bounds */ return 0; /* out of bounds */
for (idx=0; idx < nkeys; idx++) for (idx=0; idx < nkeys; idx++)
@ -313,7 +313,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
if (keyinfolen < 28) if (keyinfolen < 28)
return 0; /* invalid blob */ return 0; /* invalid blob */
pos = 20 + keyinfolen*nkeys; pos = 20 + keyinfolen*nkeys;
if (pos+2 > length) if ((uint64_t)pos+2 > (uint64_t)length)
return 0; /* out of bounds */ return 0; /* out of bounds */
/*serial*/ /*serial*/
@ -340,7 +340,7 @@ blob_cmp_name (KEYBOXBLOB blob, int idx,
mypos += idx*uidinfolen; mypos += idx*uidinfolen;
off = get32 (buffer+mypos); off = get32 (buffer+mypos);
len = get32 (buffer+mypos+4); 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 */ return 0; /* error: better stop here out of bounds */
if (len < 1) if (len < 1)
continue; /* empty name */ continue; /* empty name */
@ -439,7 +439,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
mypos += idx*uidinfolen; mypos += idx*uidinfolen;
off = get32 (buffer+mypos); off = get32 (buffer+mypos);
len = get32 (buffer+mypos+4); 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 */ return 0; /* error: better stop here - out of bounds */
if (x509) if (x509)
{ {
@ -522,7 +522,7 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
return 0; /* Too short. */ return 0; /* Too short. */
cert_off = get32 (buffer+8); cert_off = get32 (buffer+8);
cert_len = get32 (buffer+12); 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. */ return 0; /* Too short. */
rc = ksba_reader_new (&reader); 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); return gpg_error (GPG_ERR_TOO_SHORT);
image_off = get32 (buffer+8); image_off = get32 (buffer+8);
image_len = get32 (buffer+12); 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); return gpg_error (GPG_ERR_TOO_SHORT);
err = _keybox_get_flag_location (buffer, length, KEYBOX_FLAG_SIG_INFO, 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); return gpg_error (GPG_ERR_TOO_SHORT);
cert_off = get32 (buffer+8); cert_off = get32 (buffer+8);
cert_len = get32 (buffer+12); 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); return gpg_error (GPG_ERR_TOO_SHORT);
rc = ksba_reader_new (&reader); rc = ksba_reader_new (&reader);

View File

@ -348,7 +348,8 @@ get_cached_data (app_t app, int tag,
err = iso7816_get_data (app->slot, exmode, tag, &p, &len); err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
if (err) if (err)
return err; return err;
*result = p; if (len)
*result = p;
*resultlen = len; *resultlen = len;
/* Check whether we should cache this object. */ /* Check whether we should cache this object. */
@ -370,7 +371,10 @@ get_cached_data (app_t app, int tag,
c = xtrymalloc (sizeof *c + len); c = xtrymalloc (sizeof *c + len);
if (c) if (c)
{ {
memcpy (c->data, p, len); if (len)
memcpy (c->data, p, len);
else
xfree (p);
c->length = len; c->length = len;
c->tag = tag; c->tag = tag;
c->next = app->app_local->cache; 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; size_t buflen;
if (app->app_local->extcap.kdf_do 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; char *salt;
unsigned long s2k_count; unsigned long s2k_count;

View File

@ -1848,7 +1848,8 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
p = buf; p = buf;
n = 0; 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); valuelen = va_arg (arg_ptr, size_t);
if (!valuelen) if (!valuelen)
@ -1865,6 +1866,7 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
{ {
sprintf (p, "%%%02X", *value); sprintf (p, "%%%02X", *value);
p += 3; p += 3;
n += 2;
} }
else if (*value == ' ') else if (*value == ' ')
*p++ = '+'; *p++ = '+';

View File

@ -236,6 +236,10 @@ static HANDLE the_event;
/* PID to notify update of usb devices. */ /* PID to notify update of usb devices. */
static pid_t main_thread_pid; static pid_t main_thread_pid;
#endif #endif
#ifdef HAVE_PSELECT_NO_EINTR
/* FD to notify changes. */
static int notify_fd;
#endif
static char *create_socket_name (char *standard_name); static char *create_socket_name (char *standard_name);
static gnupg_fd_t create_server_socket (const char *name, static gnupg_fd_t create_server_socket (const char *name,
@ -1210,6 +1214,8 @@ scd_kick_the_loop (void)
if (ret == 0) if (ret == 0)
log_error ("SetEvent for scd_kick_the_loop failed: %s\n", log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
w32_strerror (-1)); w32_strerror (-1));
#elif defined(HAVE_PSELECT_NO_EINTR)
write (notify_fd, "", 1);
#else #else
ret = kill (main_thread_pid, SIGCONT); ret = kill (main_thread_pid, SIGCONT);
if (ret < 0) if (ret < 0)
@ -1241,6 +1247,17 @@ handle_connections (int listen_fd)
#else #else
int signo; int signo;
#endif #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); ret = npth_attr_init(&tattr);
if (ret) if (ret)
@ -1298,6 +1315,7 @@ handle_connections (int listen_fd)
for (;;) for (;;)
{ {
int periodical_check; int periodical_check;
int max_fd = nfd;
if (shutdown_pending) if (shutdown_pending)
{ {
@ -1326,8 +1344,14 @@ handle_connections (int listen_fd)
thus a simple assignment is fine to copy the entire set. */ thus a simple assignment is fine to copy the entire set. */
read_fdset = fdset; 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 #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 ()); npth_sigev_sigmask ());
saved_errno = errno; saved_errno = errno;
@ -1353,6 +1377,15 @@ handle_connections (int listen_fd)
/* Timeout. Will be handled when calculating the next timeout. */ /* Timeout. Will be handled when calculating the next timeout. */
continue; 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)) if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
{ {
ctrl_t ctrl; ctrl_t ctrl;
@ -1393,6 +1426,10 @@ handle_connections (int listen_fd)
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
if (the_event != INVALID_HANDLE_VALUE) if (the_event != INVALID_HANDLE_VALUE)
CloseHandle (the_event); CloseHandle (the_event);
#endif
#ifdef HAVE_PSELECT_NO_EINTR
close (pipe_fd[0]);
close (pipe_fd[1]);
#endif #endif
cleanup (); cleanup ();
log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); log_info (_("%s %s stopped\n"), strusage(11), strusage(13));

View File

@ -479,6 +479,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode)
leave: leave:
gnupg_ksba_destroy_writer (b64writer); gnupg_ksba_destroy_writer (b64writer);
ksba_cert_release (cert); ksba_cert_release (cert);
xfree (keygrip);
xfree (desc); xfree (desc);
keydb_release (hd); 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) */ array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */
gcry_mpi_sub_ui (array[6], array[4], 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; return array;
} }

View File

@ -31,6 +31,7 @@
#include <assuan.h> #include <assuan.h>
#include "../common/sysutils.h" #include "../common/sysutils.h"
#include "../common/server-help.h" #include "../common/server-help.h"
#include "../common/asshelp.h"
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) #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 else
{ {
assuan_context_t ctx = ctrl->server_local->assuan_ctx; err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
char buf[950], *p; get_status_string (no), arg_ptr);
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);
} }
va_end (arg_ptr); va_end (arg_ptr);

View File

@ -116,7 +116,7 @@
#endif #endif
#if __STDC_VERSION__ < 199901L #if __STDC_VERSION__ < 199901L
# if __GNUC__ >= 2 # if __GNUC__ >= 2 && !defined (__func__)
# define __func__ __FUNCTION__ # define __func__ __FUNCTION__
# else # else
/* Let's try our luck here. Some systems may provide __func__ without /* Let's try our luck here. Some systems may provide __func__ without

View File

@ -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. */ /* Add the user id UID to the key identified by FINGERPRINT. */
static gpg_error_t static gpg_error_t
add_user_id (const char *fingerprint, const char *uid) 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); err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave; goto leave;
} }
err = get_key (&key, fingerprint, addrspec, 0); err = wks_get_key (&key, fingerprint, addrspec, 0);
if (err) if (err)
goto leave; goto leave;
@ -782,27 +669,19 @@ command_send (const char *fingerprint, const char *userid)
err = 0; err = 0;
} }
else 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; estream_t mbuf;
err = wkd_get_policy_flags (addrspec, &mbuf); err = wkd_get_policy_flags (addrspec, &mbuf);
if (err && gpg_err_code (err) != GPG_ERR_NO_DATA) if (err && gpg_err_code (err) != GPG_ERR_NO_DATA)
{ {
log_error ("error reading policy flags for '%s': %s\n", log_error ("error reading policy flags for '%s': %s\n",
submission_to, gpg_strerror (err)); domain, gpg_strerror (err));
goto leave; goto leave;
} }
if (mbuf) if (mbuf)
@ -812,8 +691,35 @@ command_send (const char *fingerprint, const char *userid)
if (err) if (err)
goto leave; 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) if (policy.auth_submit)
log_info ("no confirmation required for '%s'\n", addrspec); log_info ("no confirmation required for '%s'\n", addrspec);
@ -853,7 +759,7 @@ command_send (const char *fingerprint, const char *userid)
estream_t newkey; estream_t newkey;
es_rewind (key); es_rewind (key);
err = wks_filter_uid (&newkey, key, thisuid->uid); err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
if (err) if (err)
{ {
log_error ("error filtering key: %s\n", gpg_strerror (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. */ * the key again. */
es_fclose (key); es_fclose (key);
key = NULL; key = NULL;
err = get_key (&key, fingerprint, addrspec, 1); err = wks_get_key (&key, fingerprint, addrspec, 1);
if (err) if (err)
goto leave; goto leave;
} }
@ -1002,6 +908,7 @@ command_send (const char *fingerprint, const char *userid)
free_uidinfo_list (uidlist); free_uidinfo_list (uidlist);
es_fclose (keyenc); es_fclose (keyenc);
es_fclose (key); es_fclose (key);
wks_free_policy (&policy);
xfree (addrspec); xfree (addrspec);
return err; return err;
} }

View File

@ -1,5 +1,5 @@
/* gpg-wks-server.c - A server for the Web Key Service protocols. /* 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 * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -20,7 +20,7 @@
/* The Web Key Service I-D defines an update protocol to store a /* The Web Key Service I-D defines an update protocol to store a
* public key in the Web Key Directory. The current specification is * 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> #include <config.h>
@ -35,6 +35,7 @@
#include "../common/util.h" #include "../common/util.h"
#include "../common/init.h" #include "../common/init.h"
#include "../common/sysutils.h" #include "../common/sysutils.h"
#include "../common/userids.h"
#include "../common/ccparray.h" #include "../common/ccparray.h"
#include "../common/exectool.h" #include "../common/exectool.h"
#include "../common/zb32.h" #include "../common/zb32.h"
@ -154,7 +155,7 @@ static gpg_error_t command_receive_cb (void *opaque,
const char *mediatype, estream_t fp, const char *mediatype, estream_t fp,
unsigned int flags); unsigned int flags);
static gpg_error_t command_list_domains (void); 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_remove_key (const char *mailaddr);
static gpg_error_t command_revoke_key (const char *mailaddr); static gpg_error_t command_revoke_key (const char *mailaddr);
static gpg_error_t command_check_key (const char *mailaddr); static gpg_error_t command_check_key (const char *mailaddr);
@ -376,9 +377,9 @@ main (int argc, char **argv)
break; break;
case aInstallKey: case aInstallKey:
if (argc != 1) if (argc != 2)
wrong_args ("--install-key FILE"); wrong_args ("--install-key FILE USER-ID");
err = command_install_key (*argv); err = command_install_key (*argv, argv[1]);
break; break;
case aRemoveKey: case aRemoveKey:
@ -1135,6 +1136,8 @@ process_new_key (server_ctx_t ctx, estream_t key)
char *fname = NULL; char *fname = NULL;
struct policy_flags_s policybuf; struct policy_flags_s policybuf;
memset (&policybuf, 0, sizeof policybuf);
/* First figure out the user id from the key. */ /* First figure out the user id from the key. */
xfree (ctx->fpr); xfree (ctx->fpr);
free_uidinfo_list (ctx->mboxes); free_uidinfo_list (ctx->mboxes);
@ -1206,6 +1209,7 @@ process_new_key (server_ctx_t ctx, estream_t key)
xfree (nonce); xfree (nonce);
xfree (fname); xfree (fname);
xfree (dname); xfree (dname);
wks_free_policy (&policybuf);
return err; 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. */ /* Check that we have send a request with NONCE and publish the key. */
static gpg_error_t static gpg_error_t
check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) 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; goto leave;
} }
/* Hash user ID and create filename. */ /* Hash user ID and create filename. */
s = strchr (address, '@'); err = compute_hu_fname (&fnewname, address);
log_assert (s); if (err)
gcry_md_hash_buffer (GCRY_MD_SHA1, shaxbuf, address, s - address); goto leave;
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;
}
/* Publish. */ /* Publish. */
err = copy_key_as_binary (fname, fnewname, address); err = copy_key_as_binary (fname, fnewname, address);
@ -1897,6 +1962,7 @@ command_list_domains (void)
if (!memcmp (&empty_policy, &policy, sizeof policy)) if (!memcmp (&empty_policy, &policy, sizeof policy))
log_error ("domain %s: empty policy file\n", domain); 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 static gpg_error_t
command_install_key (const char *fname) command_install_key (const char *fname, const char *userid)
{ {
(void)fname; gpg_error_t err;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); 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. */ * R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. */
static gpg_error_t static gpg_error_t
fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) fname_from_userid (const char *userid, char **r_fname, char **r_addrspec)

View File

@ -60,6 +60,7 @@ struct
/* The parsed policy flags. */ /* The parsed policy flags. */
struct policy_flags_s struct policy_flags_s
{ {
char *submission_address;
unsigned int mailbox_only : 1; unsigned int mailbox_only : 1;
unsigned int dane_only : 1; unsigned int dane_only : 1;
unsigned int auth_submit : 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_set_status_fd (int fd);
void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
void free_uidinfo_list (uidinfo_list_t list); 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, gpg_error_t wks_list_key (estream_t key, char **r_fpr,
uidinfo_list_t *r_mboxes); uidinfo_list_t *r_mboxes);
gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key, 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_send_mime (mime_maker_t mime);
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
int ignore_unknown); int ignore_unknown);
void wks_free_policy (policy_flags_t policy);
/*-- wks-receive.c --*/ /*-- wks-receive.c --*/

View File

@ -132,6 +132,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. */ /* Helper for wks_list_key and wks_filter_uid. */
static void static void
@ -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 /* 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 * stored at R_NEWKEY. The new key will contain only the user id UID.
* UID. Returns 0 on success. Only one key is expected in KEY. */ * 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 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; gpg_error_t err;
ccparray_t ccp; 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. */ /* Prefix the key with the MIME content type. */
es_fputs ("Content-Type: application/pgp-keys\n" if (!binary)
"\n", newkey); es_fputs ("Content-Type: application/pgp-keys\n"
"\n", newkey);
filterexp = es_bsprintf ("keep-uid=uid=%s", uid); filterexp = es_bsprintf ("keep-uid=uid=%s", uid);
if (!filterexp) 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, "--batch");
ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--status-fd=2");
ccparray_put (&ccp, "--always-trust"); 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-options=import-export");
ccparray_put (&ccp, "--import-filter"); ccparray_put (&ccp, "--import-filter");
ccparray_put (&ccp, filterexp); ccparray_put (&ccp, filterexp);
@ -443,6 +562,7 @@ gpg_error_t
wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
{ {
enum tokens { enum tokens {
TOK_SUBMISSION_ADDRESS,
TOK_MAILBOX_ONLY, TOK_MAILBOX_ONLY,
TOK_DANE_ONLY, TOK_DANE_ONLY,
TOK_AUTH_SUBMIT, TOK_AUTH_SUBMIT,
@ -453,6 +573,7 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
const char *name; const char *name;
enum tokens token; enum tokens token;
} keywords[] = { } keywords[] = {
{ "submission-address", TOK_SUBMISSION_ADDRESS },
{ "mailbox-only", TOK_MAILBOX_ONLY }, { "mailbox-only", TOK_MAILBOX_ONLY },
{ "dane-only", TOK_DANE_ONLY }, { "dane-only", TOK_DANE_ONLY },
{ "auth-submit", TOK_AUTH_SUBMIT }, { "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) 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_MAILBOX_ONLY: flags->mailbox_only = 1; break;
case TOK_DANE_ONLY: flags->dane_only = 1; break; case TOK_DANE_ONLY: flags->dane_only = 1; break;
case TOK_AUTH_SUBMIT: flags->auth_submit = 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; return err;
} }
void
wks_free_policy (policy_flags_t policy)
{
if (policy)
{
xfree (policy->submission_address);
memset (policy, 0, sizeof *policy);
}
}