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
=========
- 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
+Noteworthy changes in version 2.2.5 (2018-02-22)
+------------------------------------------------
+
+ * gpg: Allow the use of the "cv25519" and "ed25519" short names in
+ addition to the canonical curve names in --batch --gen-key.
+
+ * gpg: Make sure to print all secret keys with option --list-only
+ and --decrypt. [#3718]
+
+ * gpg: Fix the use of future-default with --quick-add-key for
+ signing keys. [#3747]
+
+ * gpg: Select a secret key by checking availability under gpg-agent.
+ [#1967]
+
+ * gpg: Fix reversed prompt texts for --only-sign-text-ids. [#3787]
+
+ * gpg,gpgsm: Fix detection of bogus keybox blobs on 32 bit systems.
+ [#3770]
+
+ * gpgsm: Fix regression since 2.1 in --export-secret-key-raw which
+ got $d mod (q-1)$ wrong. Note that most tools automatically fixup
+ that parameter anyway.
+
+ * ssh: Fix a regression in getting the client'd PID on *BSD and
+ macOS.
+
+ * scd: Support the KDF Data Object of the OpenPGP card 3.3. [#3152]
+
+ * scd: Fix a regression in the internal CCID driver for certain card
+ readers. [#3508]
+
+ * scd: Fix a problem on NetBSD killing scdaemon on gpg-agent
+ shutdown. [#3778]
+
+ * dirmngr: Improve returned error description on failure of DNS
+ resolving. [#3756]
+
+ * wks: Implement command --install-key for gpg-wks-server.
+
+ * Add option STATIC=1 to the Speedo build system to allow a build
+ with statically linked versions of the core GnuPG libraries. Also
+ use --enable-wks-tools by default by Speedo builds for Unix.
+
+
Noteworthy changes in version 2.2.4 (2017-12-20)
------------------------------------------------
* New configure option --enable-run-gnupg-user-socket to first try a
socket directory which is not removed by systemd at session end.
+ See-also: gnupg-announce/2017q4/000419.html
+
Noteworthy changes in version 2.2.3 (2017-11-20)
------------------------------------------------
=========================
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
#include <sys/socket.h>
#include <sys/un.h>
#endif /*!HAVE_W32_SYSTEM*/
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
#ifdef HAVE_UCRED_H
#include <ucred.h>
#endif
/* 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;
}
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);
}
}
# autogen.sh configuration for GnuPG -*- sh -*-
+display_name=GnuPG
+
#version_parts=3
case "$myhost:$myhostsub" in
#! /bin/sh
# autogen.sh
-# Copyright (C) 2003, 2014, 2017 g10 Code GmbH
+# Copyright (C) 2003, 2014, 2017, 2018 g10 Code GmbH
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# configure it for the respective package. It is maintained as part of
# GnuPG and source copied by other packages.
#
-# Version: 2017-01-17
+# Version: 2018-02-21
configure_ac="configure.ac"
EOF
$CP build-aux/git-hooks/commit-msg .git/hooks/commit-msg
chmod +x .git/hooks/commit-msg
+ if [ x"${display_name}" != x ]; then
+ git config format.subjectPrefix "PATCH ${display_name}"
+ fi
fi
fi
@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.'
# 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
endif
endif
+ifeq ($(STATIC),0)
speedo_spkgs += \
gpgme
+endif
ifeq ($(TARGETOS),w32)
ifeq ($(WITH_GUI),1)
# Package build options
#
+speedo_pkg_npth_configure = --enable-static
+
speedo_pkg_libgpg_error_configure = --enable-static
speedo_pkg_w64_libgpg_error_configure = --enable-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
#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*/
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*/
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;
+}
};
int i;
struct stat sb;
- char prefix[13 + 1 + 20 + 6 + 1];
+ char prefix[19 + 1 + 20 + 6 + 1];
const char *s;
char *name = NULL;
void log_clock (const char *string);
+/* Some handy assertion macros which don't abort. */
+
+#define return_if_fail(expr) do { \
+ if (!(expr)) { \
+ log_debug ("%s:%d: assertion '%s' failed\n", \
+ __FILE__, __LINE__, #expr ); \
+ return; \
+ } } while (0)
+#define return_null_if_fail(expr) do { \
+ if (!(expr)) { \
+ log_debug ("%s:%d: assertion '%s' failed\n", \
+ __FILE__, __LINE__, #expr ); \
+ return NULL; \
+ } } while (0)
+#define return_val_if_fail(expr,val) do { \
+ if (!(expr)) { \
+ log_debug ("%s:%d: assertion '%s' failed\n", \
+ __FILE__, __LINE__, #expr ); \
+ return (val); \
+ } } while (0)
+#define never_reached() do { \
+ log_debug ("%s:%d: oops - should never get here\n", \
+ __FILE__, __LINE__ ); \
+ } while (0)
+
+
#endif /*GNUPG_COMMON_LOGGING_H*/
#define W32INFO_PRODUCTVERSION "@VERSION@\0"
#define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \
-2017 Free Software Foundation, Inc.\0"
+2018 Free Software Foundation, Inc.\0"
# 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.
#
m4_define([mym4_package],[gnupg])
m4_define([mym4_major], [2])
m4_define([mym4_minor], [2])
-m4_define([mym4_micro], [4])
+m4_define([mym4_micro], [5])
# To start a new development series, i.e a new major or minor number
# you need to mark an arbitrary commit before the first beta release
NEED_LIBGCRYPT_VERSION=1.7.0
NEED_LIBASSUAN_API=2
-NEED_LIBASSUAN_VERSION=2.4.3
+NEED_LIBASSUAN_VERSION=2.5.0
NEED_KSBA_API=1
NEED_KSBA_VERSION=1.3.4
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
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
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}"
AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \
pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h \
stdint.h signal.h util.h libutil.h termios.h \
- ucred.h sys/sysmacros.h sys/mkdev.h])
+ ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h])
AC_HEADER_TIME
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;
}
{
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);
[gpgsm]
enable-crl-checks
+compliance de-vs
[gpg-agent]
enable-extended-key-format
@table @gnupgtabopt
@item --quick-generate-key @var{user-id} [@var{algo} [@var{usage} [@var{expire}]]]
+@itemx --quick-gen-key
@opindex quick-generate-key
+@opindex quick-gen-key
This is a simple command to generate a standard key with one user id.
In contrast to @option{--generate-key} the key is generated directly
without the need to answer a bunch of prompts. Unless the option
the passphrase will be read from STDIN. This can only be used if only
one passphrase is supplied.
-Note that this passphrase is only used if the option @option{--batch}
-has also been given. This is different from GnuPG version 1.x.
+Note that since Version 2.0 this passphrase is only used if the
+option @option{--batch} has also been given. Since Version 2.1
+the @option{--pinentry-mode} also needs to be set to @code{loopback}.
@item --passphrase-file @var{file}
@opindex passphrase-file
passphrase is supplied. Obviously, a passphrase stored in a file is
of questionable security if other users can read this file. Don't use
this option if you can avoid it.
-Note that this passphrase is only used if the option @option{--batch}
-has also been given. This is different from GnuPG version 1.x.
+
+Note that since Version 2.0 this passphrase is only used if the
+option @option{--batch} has also been given. Since Version 2.1
+the @option{--pinentry-mode} also needs to be set to @code{loopback}.
@item --passphrase @var{string}
@opindex passphrase
passphrase is supplied. Obviously, this is of very questionable
security on a multi-user system. Don't use this option if you can
avoid it.
-Note that this passphrase is only used if the option @option{--batch}
-has also been given. This is different from GnuPG version 1.x.
+
+Note that since Version 2.0 this passphrase is only used if the
+option @option{--batch} has also been given. Since Version 2.1
+the @option{--pinentry-mode} also needs to be set to @code{loopback}.
@item --pinentry-mode @var{mode}
@opindex pinentry-mode
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
@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}
.RI [ options ]
.B \-\-install-key
.I file
+.I user-id
.br
.B gpg-wks-server
.RI [ options ]
@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
@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:
sec rsa2048 2016-08-30 [SC]
C0FCF8642D830C53246211400346653590B3795B
uid [ultimate] key-submission@@example.net
- bxzcxpxk8h87z1k7bzk86xn5aj47intu@@example.net
ssb rsa2048 2016-08-30 [E]
@end example
-Take the hash of the string "key-submission", which is
-"bxzcxpxk8h87z1k7bzk86xn5aj47intu" and manually publish that key:
+Take the fingerprint from that output and manually publish the key:
@example
- $ gpg --export-options export-minimal --export \
- > -o /var/lib/gnupg/wks/example.net/hu/bxzcxpxk8h87z1k7bzk86xn5aj47intu \
- > key-submission@@example.new
+ $ gpg-wks-server --install-key C0FCF8642D830C53246211400346653590B3795B \
+ > key-submission@@example.net
@end example
-Make sure that the created file is world readable.
-
Finally that submission address needs to be redirected to a script
running @command{gpg-wks-server}. The @command{procmail} command can
be used for this: Redirect the submission address to the user "webkey"
case 0x000A: return "Dangerous Things";
case 0x002A: return "Magrathea";
+ case 0x0042: return "GnuPG e.V.";
case 0x1337: return "Warsaw Hackerspace";
case 0x2342: return "warpzone"; /* hackerspace Muenster. */
+ case 0xBD0E: return "Paranoidlabs";
case 0xF517: return "FSIJ";
/* 0x0000 and 0xFFFF are defined as test cards per spec,
kbnode_t *ret_keyblock, kbnode_t *ret_found_key);
static kbnode_t finish_lookup (kbnode_t keyblock,
unsigned int req_usage, int want_exact,
- unsigned int *r_flags);
+ int want_secret, unsigned int *r_flags);
static void print_status_key_considered (kbnode_t keyblock, unsigned int flags);
/* Warning: node flag bits 0 and 1 should be preserved by
* merge_selfsigs. FIXME: Check whether this still holds. */
merge_selfsigs (ctrl, keyblock);
- found_key = finish_lookup (keyblock, pk->req_usage, 0, &infoflags);
+ found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, &infoflags);
print_status_key_considered (keyblock, infoflags);
if (found_key)
pk_from_block (pk, keyblock, found_key);
*/
static kbnode_t
finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
- unsigned int *r_flags)
+ int want_secret, unsigned int *r_flags)
{
kbnode_t k;
continue;
}
+ if (want_secret && agent_probe_secret_key (NULL, pk))
+ {
+ if (DBG_LOOKUP)
+ log_debug ("\tno secret key\n");
+ continue;
+ }
+
if (DBG_LOOKUP)
log_debug ("\tsubkey might be fine\n");
/* In case a key has a timestamp of 0 set, we make sure
* merge_selfsigs. */
merge_selfsigs (ctrl, keyblock);
found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact,
- &infoflags);
+ want_secret, &infoflags);
print_status_key_considered (keyblock, infoflags);
if (found_key)
{
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;
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)
{
if (!curve || !*curve)
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ /* Map the displayed short forms of some curves to their canonical
+ * names. */
+ if (!ascii_strcasecmp (curve, "cv25519"))
+ curve = "Curve25519";
+ else if (!ascii_strcasecmp (curve, "ed25519"))
+ curve = "Ed25519";
+
/* Note that we use the "comp" flag with EdDSA to request the use of
a 0x40 compression prefix octet. */
if (algo == PUBKEY_ALGO_EDDSA)
* success is returned. On error an error code is returned. Note
* that STRING may be modified by this function. NULL may be passed
* for any parameter. FOR_SUBKEY shall be true if this is used as a
+ * subkey. If CLEAR_CERT is set a default CERT usage will be cleared;
+ * this is useful if for example the default algorithm is used for a
* subkey. */
static gpg_error_t
-parse_key_parameter_part (char *string, int for_subkey,
+parse_key_parameter_part (char *string, int for_subkey, int clear_cert,
int *r_algo, unsigned int *r_size,
unsigned int *r_keyuse,
char const **r_curve)
if (!for_subkey)
keyuse |= PUBKEY_USAGE_CERT;
+ /* But if requested remove th cert usage. */
+ if (clear_cert)
+ keyuse &= ~PUBKEY_USAGE_CERT;
+
/* Check that usage is actually possible. */
if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT))
&& !pubkey_get_nsig (algo))
* -1 := Both parts
* 0 := Only the part of the primary key
* 1 := If there is one part parse that one, if there are
- * two parts parse the second part. Always return
- * in the args for the primary key (R_ALGO,....).
+ * two parts parse the part which best matches the
+ * SUGGESTED_USE or in case that can't be evaluated the second part.
+ * Always return using the args for the primary key (R_ALGO,....).
*
*/
gpg_error_t
parse_key_parameter_string (const char *string, int part,
+ unsigned int suggested_use,
int *r_algo, unsigned int *r_size,
- unsigned *r_keyuse,
+ unsigned int *r_keyuse,
char const **r_curve,
int *r_subalgo, unsigned int *r_subsize,
unsigned *r_subkeyuse,
*r_subcurve = NULL;
if (!string || !*string
- || !strcmp (string, "default") || !strcmp (string, "-"))
+ || !ascii_strcasecmp (string, "default") || !strcmp (string, "-"))
string = get_default_pubkey_algo ();
- else if (!strcmp (string, "future-default"))
+ else if (!ascii_strcasecmp (string, "future-default")
+ || !ascii_strcasecmp (string, "futuredefault"))
string = FUTURE_STD_KEY_PARAM;
primary = xstrdup (string);
*secondary++ = 0;
if (part == -1 || part == 0)
{
- err = parse_key_parameter_part (primary, 0, r_algo, r_size,
+ err = parse_key_parameter_part (primary, 0, 0, r_algo, r_size,
r_keyuse, r_curve);
if (!err && part == -1)
- err = parse_key_parameter_part (secondary, 1, r_subalgo, r_subsize,
+ err = parse_key_parameter_part (secondary, 1, 0, r_subalgo, r_subsize,
r_subkeyuse, r_subcurve);
}
else if (part == 1)
{
/* If we have SECONDARY, use that part. If there is only one
- * part consider this to be the subkey algo. */
- err = parse_key_parameter_part (secondary? secondary : primary, 1,
- r_algo, r_size, r_keyuse, r_curve);
+ * part consider this to be the subkey algo. In case a
+ * SUGGESTED_USE has been given and the usage of the secondary
+ * part does not match SUGGESTED_USE try again using the primary
+ * part. Noet thar when falling back to the primary key we need
+ * to force clearing the cert usage. */
+ if (secondary)
+ {
+ err = parse_key_parameter_part (secondary, 1, 0,
+ r_algo, r_size, r_keyuse, r_curve);
+ if (!err && suggested_use && r_keyuse && !(suggested_use & *r_keyuse))
+ err = parse_key_parameter_part (primary, 1, 1 /*(clear cert)*/,
+ r_algo, r_size, r_keyuse, r_curve);
+ }
+ else
+ err = parse_key_parameter_part (primary, 1, 0,
+ r_algo, r_size, r_keyuse, r_curve);
}
xfree (primary);
* for the curve etc. That is a ugly but demanded for backward
* compatibility with the batch key generation. It would be
* better to make full use of parse_key_parameter_string. */
- parse_key_parameter_string (NULL, 0,
+ parse_key_parameter_string (NULL, 0, 0,
&i, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
if (!*expirestr || strcmp (expirestr, "-") == 0)
expirestr = default_expiration_interval;
- if ((!*algostr || !strcmp (algostr, "default")
- || !strcmp (algostr, "future-default"))
- && (!*usagestr || !strcmp (usagestr, "default")
+ if ((!*algostr || !ascii_strcasecmp (algostr, "default")
+ || !ascii_strcasecmp (algostr, "future-default")
+ || !ascii_strcasecmp (algostr, "futuredefault"))
+ && (!*usagestr || !ascii_strcasecmp (usagestr, "default")
|| !strcmp (usagestr, "-")))
{
/* Use default key parameters. */
unsigned int keyuse, subkeyuse;
const char *curve, *subcurve;
- err = parse_key_parameter_string (algostr, -1,
+ err = parse_key_parameter_string (algostr, -1, 0,
&algo, &size, &keyuse, &curve,
&subalgo, &subsize, &subkeyuse,
&subcurve);
#endif
, "--full-generate-key" );
- err = parse_key_parameter_string (NULL, -1,
+ err = parse_key_parameter_string (NULL, -1, 0,
&algo, &size, &keyuse, &curve,
&subalgo, &subsize,
&subkeyuse, &subcurve);
}
err = parse_key_parameter_string (algostr, for_subkey? 1 : 0,
+ usagestr? parse_usagestr (usagestr):0,
&algo, &nbits, &use, &curve,
NULL, NULL, NULL, NULL);
if (err)
/* Parse the usage string. */
if (!usagestr || !*usagestr
- || !strcmp (usagestr, "default") || !strcmp (usagestr, "-"))
+ || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-"))
; /* Keep usage from parse_key_parameter_string. */
else if ((wantuse = parse_usagestr (usagestr)) != -1)
use = wantuse;
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;
|| have_secret_key_with_kid (enc->keyid)))
{
if(opt.list_only)
- result = -1;
+ result = GPG_ERR_MISSING_ACTION; /* fixme: Use better error code. */
else
{
c->dek = xmalloc_secure_clear (sizeof *c->dek);
else
result = GPG_ERR_PUBKEY_ALGO;
- if (result == -1)
- ;
- else
+ if (1)
{
/* Store it for later display. */
struct kidlist_item *x = xmalloc (sizeof *x);
write_status_text (STATUS_NO_SECKEY, buf);
}
}
+ else if (gpg_err_code (list->reason) == GPG_ERR_MISSING_ACTION)
+ {
+ /* Not tested for secret key due to --list-only mode. */
+ }
else if (list->reason)
{
log_info (_("public key decryption failed: %s\n"),
#include "mount.h"
#include "suspend.h"
#include "../common/server-help.h"
+#include "../common/asshelp.h"
#include "../common/call-gpg.h"
}
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);
#include "g13-syshelp.h"
#include <assuan.h>
#include "../common/i18n.h"
+#include "../common/asshelp.h"
#include "keyblob.h"
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;
}
/*create_dotlock(NULL); register locking cleanup */
/* We need to use the gcry malloc function because jnlib uses them. */
- keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
#include <sys/types.h> /* off_t */
-/* We include the type definitions from jnlib instead of defining our
- owns here. This will not allow us build KBX in a standalone way
- but there is currently no need for it anyway. Same goes for
- stringhelp.h which for example provides a replacement for stpcpy -
- fixme: Better use the LIBOBJ mechnism. */
-#include "../common/types.h"
-#include "../common/stringhelp.h"
-#include "../common/dotlock.h"
-#include "../common/logging.h"
-
+#include "../common/util.h"
#include "keybox.h"
/*-- keybox-util.c --*/
-void *_keybox_malloc (size_t n);
-void *_keybox_calloc (size_t n, size_t m);
-void *_keybox_realloc (void *p, size_t n);
-void _keybox_free (void *p);
-
-#define xtrymalloc(a) _keybox_malloc ((a))
-#define xtrycalloc(a,b) _keybox_calloc ((a),(b))
-#define xtryrealloc(a,b) _keybox_realloc((a),(b))
-#define xfree(a) _keybox_free ((a))
-
-
-#define DIM(v) (sizeof(v)/sizeof((v)[0]))
-#define DIMof(type,member) DIM(((type *)0)->member)
-#ifndef STR
-# define STR(v) #v
-#endif
-#define STR2(v) STR(v)
/*
- a couple of handy macros
-*/
-
-#define return_if_fail(expr) do { \
- if (!(expr)) { \
- fprintf (stderr, "%s:%d: assertion '%s' failed\n", \
- __FILE__, __LINE__, #expr ); \
- return; \
- } } while (0)
-#define return_null_if_fail(expr) do { \
- if (!(expr)) { \
- fprintf (stderr, "%s:%d: assertion '%s' failed\n", \
- __FILE__, __LINE__, #expr ); \
- return NULL; \
- } } while (0)
-#define return_val_if_fail(expr,val) do { \
- if (!(expr)) { \
- fprintf (stderr, "%s:%d: assertion '%s' failed\n", \
- __FILE__, __LINE__, #expr ); \
- return (val); \
- } } while (0)
-#define never_reached() do { \
- fprintf (stderr, "%s:%d: oops; should never get here\n", \
- __FILE__, __LINE__ ); \
- } while (0)
-
-
-/* some macros to replace ctype ones and avoid locale problems */
-#define digitp(p) (*(p) >= '0' && *(p) <= '9')
-#define hexdigitp(a) (digitp (a) \
- || (*(a) >= 'A' && *(a) <= 'F') \
- || (*(a) >= 'a' && *(a) <= 'f'))
-/* the atoi macros assume that the buffer has only valid digits */
-#define atoi_1(p) (*(p) - '0' )
-#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
-#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
-#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
- *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
-#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+ * A couple of handy macros
+ */
#endif /*KEYBOX_DEFS_H*/
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++)
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++)
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*/
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 */
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)
{
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);
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,
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);
#endif
#include "keybox-defs.h"
-#include "../common/utilproto.h"
-
-
-static void *(*alloc_func)(size_t n) = malloc;
-static void *(*realloc_func)(void *p, size_t n) = realloc;
-static void (*free_func)(void*) = free;
-
-
-
-void
-keybox_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
- void *(*new_realloc_func)(void *p, size_t n),
- void (*new_free_func)(void*) )
-{
- alloc_func = new_alloc_func;
- realloc_func = new_realloc_func;
- free_func = new_free_func;
-}
-
-void *
-_keybox_malloc (size_t n)
-{
- return alloc_func (n);
-}
-
-void *
-_keybox_realloc (void *a, size_t n)
-{
- return realloc_func (a, n);
-}
-
-void *
-_keybox_calloc (size_t n, size_t m)
-{
- void *p = _keybox_malloc (n*m);
- if (p)
- memset (p, 0, n* m);
- return p;
-}
-
-void
-_keybox_free (void *p)
-{
- if (p)
- free_func (p);
-}
/* Store the two malloced temporary file names used for keybox updates
*r_tmpname = tmp_name;
return 0;
}
-
-gpg_error_t
-keybox_file_rename (const char *oldname, const char *newname,
- int *block_signals)
-{
- return gnupg_rename_file (oldname, newname, block_signals);
-}
/*-- keybox-util.c --*/
-void keybox_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
- void *(*new_realloc_func)(void *p, size_t n),
- void (*new_free_func)(void*) );
-
gpg_error_t keybox_tmp_names (const char *filename, int for_keyring,
char **r_bakname, char **r_tmpname);
/* V3.0 */
{ 0x7F74, 0, 0, 1, 0, 0, 0, 0, "General Feature Management"},
{ 0x00D5, 0, 0, 1, 0, 0, 0, 0, "AES key data"},
+ { 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"},
{ 0 }
};
unsigned int private_dos:1;
unsigned int algo_attr_change:1; /* Algorithm attributes changeable. */
unsigned int has_decrypt:1; /* Support symmetric decryption. */
- unsigned int kdf_do:1; /* Support KDF DOs. */
+ unsigned int kdf_do:1; /* Support KDF DO. */
unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */
unsigned int pin_blk2:1; /* PIN block 2 format supported. */
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. */
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;
{ "PRIVATE-DO-4", 0x0104 },
{ "$AUTHKEYID", 0x0000, -3 },
{ "$DISPSERIALNO",0x0000, -4 },
+ { "KDF", 0x00F9 },
{ NULL, 0 }
};
int idx, i, rc;
return result;
}
+/* Compute hash if KDF-DO is available. CHVNO must be 0 for reset
+ code, 1 or 2 for user pin and 3 for admin pin.
+ */
+static gpg_error_t
+pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
+{
+ gpg_error_t err = 0;
+ void *relptr;
+ unsigned char *buffer;
+ size_t buflen;
+
+ if (app->app_local->extcap.kdf_do
+ && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL))
+ && buflen == 110 && (buffer[2] == 0x03))
+ {
+ char *salt;
+ unsigned long s2k_count;
+ char dek[32];
+
+ salt = &buffer[(chvno==3 ? 34 : (chvno==0 ? 24 : 14))];
+ s2k_count = (((unsigned int)buffer[8] << 24)
+ | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
+ err = gcry_kdf_derive (pinvalue, strlen (pinvalue),
+ GCRY_KDF_ITERSALTED_S2K,
+ DIGEST_ALGO_SHA256, salt, 8,
+ s2k_count, sizeof (dek), dek);
+ if (!err)
+ {
+ /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */
+ *r_pinlen = 32;
+ memcpy (pinvalue, dek, *r_pinlen);
+ wipememory (dek, *r_pinlen);
+ }
+
+ xfree (relptr);
+ }
+ else
+ *r_pinlen = strlen (pinvalue);
+
+ return err;
+}
+
/* Verify a CHV either using the pinentry or if possible by
using a pinpad. PINCB and PINCB_ARG describe the usual callback
static gpg_error_t
verify_a_chv (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
- void *pincb_arg,
- int chvno, unsigned long sigcount, char **pinvalue)
+ void *pincb_arg, int chvno, unsigned long sigcount,
+ char **pinvalue, int *pinlen)
{
int rc = 0;
char *prompt_buffer = NULL;
log_assert (chvno == 1 || chvno == 2);
*pinvalue = NULL;
+ *pinlen = 0;
remaining = get_remaining_tries (app, 0);
if (remaining == -1)
return gpg_error (GPG_ERR_BAD_PIN);
}
- rc = iso7816_verify (app->slot, 0x80+chvno,
- *pinvalue, strlen (*pinvalue));
+ rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen);
+ if (!rc)
+ rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen);
}
if (rc)
{
int rc;
char *pinvalue;
+ int pinlen;
if (app->did_chv2)
return 0; /* We already verified CHV2. */
- rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
+ rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen);
if (rc)
return rc;
app->did_chv2 = 1;
the card is not configured to require a verification before
each CHV1 controlled operation (force_chv1) and if we are not
using the pinpad (PINVALUE == NULL). */
- rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen);
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
else
{
char *pinvalue;
+ int pinlen;
rc = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
return gpg_error (GPG_ERR_BAD_PIN);
}
- rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+ rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen);
xfree (pinvalue);
}
{ "SM-KEY-MAC", 0x00D2, 3, 0, 1 },
{ "KEY-ATTR", 0, 0, 3, 1 },
{ "AESKEY", 0x00D5, 3, 0, 1 },
+ { "KDF", 0x00F9, 3, 0, 1 },
{ NULL, 0 }
};
int exmode;
pininfo_t pininfo;
int use_pinpad = 0;
int minlen = 6;
+ int pinlen0 = 0;
+ int pinlen = 0;
(void)ctrl;
memset (&pininfo, 0, sizeof pininfo);
rc = gpg_error_from_syserror ();
else
{
- strcpy (stpcpy (buffer, resetcode), pinvalue);
- rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
- buffer, strlen (buffer));
- wipememory (buffer, strlen (buffer));
+ strcpy (buffer, resetcode);
+ rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0);
+ if (!rc)
+ {
+ strcpy (buffer+pinlen0, pinvalue);
+ rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen);
+ }
+ if (!rc)
+ rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+ buffer, pinlen0+pinlen);
+ wipememory (buffer, pinlen0 + pinlen);
xfree (buffer);
}
}
rc = gpg_error (GPG_ERR_BAD_PIN);
}
else
- rc = iso7816_put_data (app->slot, 0, 0xD3,
- pinvalue, strlen (pinvalue));
+ {
+ rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen);
+ }
}
else if (reset_mode)
{
- rc = iso7816_reset_retry_counter (app->slot, 0x81,
- pinvalue, strlen (pinvalue));
+ rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen);
if (!rc && !app->app_local->extcap.is_v2)
- rc = iso7816_reset_retry_counter (app->slot, 0x82,
- pinvalue, strlen (pinvalue));
+ rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen);
}
else if (!app->app_local->extcap.is_v2)
{
pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
}
else
- rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
- oldpinvalue, strlen (oldpinvalue),
- pinvalue, strlen (pinvalue));
+ {
+ rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0);
+ if (!rc)
+ rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+ oldpinvalue, pinlen0,
+ pinvalue, pinlen);
+ }
}
if (pinvalue)
{
- wipememory (pinvalue, strlen (pinvalue));
+ wipememory (pinvalue, pinlen);
xfree (pinvalue);
}
if (rc)
}
if (oldpinvalue)
{
- wipememory (oldpinvalue, strlen (oldpinvalue));
+ wipememory (oldpinvalue, pinlen0);
xfree (oldpinvalue);
}
return rc;
if (!app->did_chv1 || app->force_chv1 )
{
char *pinvalue;
+ int pinlen;
- rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue);
+ rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen);
if (rc)
return rc;
pinpad has been used. */
if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
{
- rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+ rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen);
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
}
libusb_free_transfer (handle->transfer);
+ handle->transfer = NULL;
}
libusb_release_interface (handle->idev, handle->ifc_no);
--ccid_usb_thread_is_alive;
/*
* Communication failure by device side.
* Possibly, it was forcibly suspended and resumed.
+ *
+ * Only detect this kind of failure when interrupt transfer is
+ * not supported. For card reader with interrupt transfer
+ * support removal is detected by intr_cb.
*/
- DEBUGOUT ("CCID: card inactive/removed\n");
- if (handle->transfer == NULL)
+ if (handle->ep_intr < 0)
{
+ DEBUGOUT ("CCID: card inactive/removed\n");
handle->powered_off = 1;
scd_kick_the_loop ();
}
if (statusbits == 2)
return CCID_DRIVER_ERR_NO_CARD;
+ /*
+ * In the first invocation of ccid_slot_status, card reader may
+ * return CCID_DRIVER_ERR_CARD_INACTIVE and handle->powered_off may
+ * become 1. Because inactive card is no problem (we are turning it
+ * ON here), clear the flag.
+ */
+ handle->powered_off = 0;
+
/* For an inactive and also for an active card, issue the PowerOn
command to get the ATR. */
again:
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)
{
sprintf (p, "%%%02X", *value);
p += 3;
+ n += 2;
}
else if (*value == ' ')
*p++ = '+';
/* PID to notify update of usb devices. */
static pid_t main_thread_pid;
#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+/* FD to notify changes. */
+static int notify_fd;
+#endif
\f
static char *create_socket_name (char *standard_name);
static gnupg_fd_t create_server_socket (const char *name,
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)
#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)
for (;;)
{
int periodical_check;
+ int max_fd = nfd;
if (shutdown_pending)
{
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;
/* 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;
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);
leave:
gnupg_ksba_destroy_writer (b64writer);
ksba_cert_release (cert);
+ xfree (keygrip);
xfree (desc);
keydb_release (hd);
}
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;
}
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
setup_libassuan_logging (&opt.debug, NULL);
- keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
-
/* Setup a default control structure for command line mode */
memset (&ctrl, 0, sizeof ctrl);
gpgsm_init_default_ctrl (&ctrl);
#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))
}
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);
#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
\f
-struct get_key_status_parm_s
-{
- const char *fpr;
- int found;
- int count;
-};
-
-static void
-get_key_status_cb (void *opaque, const char *keyword, char *args)
-{
- struct get_key_status_parm_s *parm = opaque;
-
- /*log_debug ("%s: %s\n", keyword, args);*/
- if (!strcmp (keyword, "EXPORTED"))
- {
- parm->count++;
- if (!ascii_strcasecmp (args, parm->fpr))
- parm->found = 1;
- }
-}
-
-
-/* Get a key by fingerprint from gpg's keyring and make sure that the
- * mail address ADDRSPEC is included in the key. If EXACT is set the
- * returned user id must match Addrspec exactly and not just in the
- * addr-spec (mailbox) part. The key is returned as a new memory
- * stream at R_KEY. */
-static gpg_error_t
-get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
- int exact)
-{
- gpg_error_t err;
- ccparray_t ccp;
- const char **argv = NULL;
- estream_t key = NULL;
- struct get_key_status_parm_s parm;
- char *filterexp = NULL;
-
- memset (&parm, 0, sizeof parm);
-
- *r_key = NULL;
-
- key = es_fopenmem (0, "w+b");
- if (!key)
- {
- err = gpg_error_from_syserror ();
- log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- /* Prefix the key with the MIME content type. */
- es_fputs ("Content-Type: application/pgp-keys\n"
- "\n", key);
-
- filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
- if (!filterexp)
- {
- err = gpg_error_from_syserror ();
- log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- ccparray_init (&ccp, 0);
-
- ccparray_put (&ccp, "--no-options");
- if (!opt.verbose)
- ccparray_put (&ccp, "--quiet");
- else if (opt.verbose > 1)
- ccparray_put (&ccp, "--verbose");
- ccparray_put (&ccp, "--batch");
- ccparray_put (&ccp, "--status-fd=2");
- ccparray_put (&ccp, "--always-trust");
- ccparray_put (&ccp, "--armor");
- ccparray_put (&ccp, "--export-options=export-minimal");
- ccparray_put (&ccp, "--export-filter");
- ccparray_put (&ccp, filterexp);
- ccparray_put (&ccp, "--export");
- ccparray_put (&ccp, "--");
- ccparray_put (&ccp, fingerprint);
-
- ccparray_put (&ccp, NULL);
- argv = ccparray_get (&ccp, NULL);
- if (!argv)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- parm.fpr = fingerprint;
- err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
- NULL, key,
- get_key_status_cb, &parm);
- if (!err && parm.count > 1)
- err = gpg_error (GPG_ERR_TOO_MANY);
- else if (!err && !parm.found)
- err = gpg_error (GPG_ERR_NOT_FOUND);
- if (err)
- {
- log_error ("export failed: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- es_rewind (key);
- *r_key = key;
- key = NULL;
-
- leave:
- es_fclose (key);
- xfree (argv);
- xfree (filterexp);
- return err;
-}
-
-
/* Add the user id UID to the key identified by FINGERPRINT. */
static gpg_error_t
add_user_id (const char *fingerprint, const char *uid)
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;
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)
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);
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));
* 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;
}
free_uidinfo_list (uidlist);
es_fclose (keyenc);
es_fclose (key);
+ wks_free_policy (&policy);
xfree (addrspec);
return err;
}
/* 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.
/* 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>
#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"
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);
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:
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);
xfree (nonce);
xfree (fname);
xfree (dname);
+ wks_free_policy (&policybuf);
return err;
}
}
+/* 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)
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);
if (!memcmp (&empty_policy, &policy, sizeof policy))
log_error ("domain %s: empty policy file\n", domain);
}
+ wks_free_policy (&policy);
}
}
-/* 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)
/* 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;
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 --*/
\f
+struct get_key_status_parm_s
+{
+ const char *fpr;
+ int found;
+ int count;
+};
+
+
+static void
+get_key_status_cb (void *opaque, const char *keyword, char *args)
+{
+ struct get_key_status_parm_s *parm = opaque;
+
+ /*log_debug ("%s: %s\n", keyword, args);*/
+ if (!strcmp (keyword, "EXPORTED"))
+ {
+ parm->count++;
+ if (!ascii_strcasecmp (args, parm->fpr))
+ parm->found = 1;
+ }
+}
+
+/* Get a key by fingerprint from gpg's keyring and make sure that the
+ * mail address ADDRSPEC is included in the key. If EXACT is set the
+ * returned user id must match Addrspec exactly and not just in the
+ * addr-spec (mailbox) part. The key is returned as a new memory
+ * stream at R_KEY. */
+gpg_error_t
+wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
+ int exact)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv = NULL;
+ estream_t key = NULL;
+ struct get_key_status_parm_s parm;
+ char *filterexp = NULL;
+
+ memset (&parm, 0, sizeof parm);
+
+ *r_key = NULL;
+
+ key = es_fopenmem (0, "w+b");
+ if (!key)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Prefix the key with the MIME content type. */
+ es_fputs ("Content-Type: application/pgp-keys\n"
+ "\n", key);
+
+ filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
+ if (!filterexp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ ccparray_init (&ccp, 0);
+
+ ccparray_put (&ccp, "--no-options");
+ if (!opt.verbose)
+ ccparray_put (&ccp, "--quiet");
+ else if (opt.verbose > 1)
+ ccparray_put (&ccp, "--verbose");
+ ccparray_put (&ccp, "--batch");
+ ccparray_put (&ccp, "--status-fd=2");
+ ccparray_put (&ccp, "--always-trust");
+ ccparray_put (&ccp, "--armor");
+ ccparray_put (&ccp, "--export-options=export-minimal");
+ ccparray_put (&ccp, "--export-filter");
+ ccparray_put (&ccp, filterexp);
+ ccparray_put (&ccp, "--export");
+ ccparray_put (&ccp, "--");
+ ccparray_put (&ccp, fingerprint);
+
+ ccparray_put (&ccp, NULL);
+ argv = ccparray_get (&ccp, NULL);
+ if (!argv)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ parm.fpr = fingerprint;
+ err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
+ NULL, key,
+ get_key_status_cb, &parm);
+ if (!err && parm.count > 1)
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ else if (!err && !parm.found)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (err)
+ {
+ log_error ("export failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ es_rewind (key);
+ *r_key = key;
+ key = NULL;
+
+ leave:
+ es_fclose (key);
+ xfree (argv);
+ xfree (filterexp);
+ return err;
+}
+
+
+\f
/* Helper for wks_list_key and wks_filter_uid. */
static void
key_status_cb (void *opaque, const char *keyword, char *args)
/* 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;
}
/* 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)
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);
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,
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 },
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;
return err;
}
+
+
+void
+wks_free_policy (policy_flags_t policy)
+{
+ if (policy)
+ {
+ xfree (policy->submission_address);
+ memset (policy, 0, sizeof *policy);
+ }
+}