Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/doc/HACKING b/doc/HACKING
index cb7e400fc..8cf49f4bd 100644
--- a/doc/HACKING
+++ b/doc/HACKING
@@ -1,445 +1,465 @@
# HACKING -*- org -*-
#+TITLE: A Hacker's Guide to GnuPG
#+TEXT: Some notes on GnuPG internals
#+STARTUP: showall
#+OPTIONS: ^:{}
# Note: This might be a copy; the original lives in gnupg/doc/HACKING.
* How to contribute
The following stuff explains some basic procedures you need to
follow if you want to contribute code or documentation.
** No more ChangeLog files
Do not modify any of the ChangeLog files in GnuPG. Starting on
December 1st, 2011 we put change information only in the GIT commit
log, and generate a top-level ChangeLog file from logs at "make dist"
time. As such, there are strict requirements on the form of the
commit log messages. The old ChangeLog files have all be renamed to
ChangeLog-2011
** Commit log requirements
Your commit log should always start with a one-line summary, the
second line should be blank, and the remaining lines are usually
ChangeLog-style entries for all affected files. However, it's fine
--- even recommended --- to write a few lines of prose describing the
change, when the summary and ChangeLog entries don't give enough of
the big picture. Omit the leading TABs that you are seeing in a
"real" ChangeLog file, but keep the maximum line length at 72 or
smaller, so that the generated ChangeLog lines, each with its leading
TAB, will not exceed 80 columns. If you want to add text which shall
not be copied to the ChangeLog, separate it by a line consisting of
two dashes at the begin of a line.
The one-line summary usually starts with a keyword to identify the
mainly affected subsystem (that is not the directory). If more than
one keyword is required they are delimited by a comma
(e.g. =scd,w32:=). Commonly found keywords are
- agent :: The gpg-agent component
- build :: Changes to the build system
- ccid :: The CCID driver in scdaemon
- common :: Code in common
- dirmngr :: The dirmngr component
- doc :: Documentation changes
- gpg :: The gpg or gpgv components
- sm :: The gpgsm component (also "gpgsm")
- gpgscm :: The regression test driver
- indent :: Indentation and similar changes
- iobuf :: The IOBUF system in common
- po :: Translations
- scd :: The scdaemon component
- speedo :: Speedo build system specific changes
- ssh :: The ssh-agent part of the agent
- tests :: The regressions tests
- tools :: Other code in tools
- w32 :: Windows related code
- wks :: The web key service tools
- yat2m :: The yat2m tool.
Typo fixes and documentation updates don't need a ChangeLog entry;
thus you would use a commit message like
#+begin_example
doc: Fix typo in a comment
--
#+end_example
The marker line here is important; without it the first line would
appear in the ChangeLog.
If you exceptionally need to have longer lines in a commit log you may
do this after this scissor line:
#+begin_example
# ------------------------ >8 ------------------------
#+end_example
(hash, blank, 24 dashes, blank, scissor, blank, 24 dashes).
Note that such a comment will be removed if the git commit option
=--cleanup=scissor= is used.
** License policy
GnuPG is licensed under the GPLv3+ with some files under a mixed
LGPLv3+/GPLv2+ license. It is thus important, that all contributed
code allows for an update of the license; for example we can't
accept code under the GPLv2(only).
GnuPG used to have a strict policy of requiring copyright
assignments to the FSF. To avoid this major organizational overhead
and to allow inclusion of code, not copyrighted by the FSF, this
policy has been relaxed on 2013-03-29. It is now also possible to
contribute code by asserting that the contribution is in accordance
to the "Libgcrypt Developer's Certificate of Origin" as found in the
file "DCO". (Except for a slight wording change, this DCO is
identical to the one used by the Linux kernel.)
If you want to contribute code or documentation to GnuPG and you
didn't sign a copyright assignment with the FSF in the past, you
need to take these simple steps:
- Decide which mail address you want to use. Please have your real
name in the address and not a pseudonym. Anonymous contributions
can only be done if you find a proxy who certifies for you.
- If your employer or school might claim ownership of code written
by you; you need to talk to them to make sure that you have the
right to contribute under the DCO.
- Send an OpenPGP signed mail to the gnupg-devel@gnupg.org mailing
list from your mail address. Include a copy of the DCO as found
in the official master branch. Insert your name and email address
into the DCO in the same way you want to use it later. Example:
Signed-off-by: Joe R. Hacker <joe@example.org>
(If you really need it, you may perform simple transformations of
the mail address: Replacing "@" by " at " or "." by " dot ".)
- That's it. From now on you only need to add a "Signed-off-by:"
line with your name and mail address to the commit message. It is
recommended to send the patches using a PGP/MIME signed mail. See
below on how to send patches.
** Coding standards
Please follow the GNU coding standards. If you are in doubt consult
the existing code as an example. Do no re-indent code without a
need. If you really need to do it, use a separate commit for such a
change.
- Only certain C99 features may be used (see below); in general
stick to C90.
- Please do not use C++ =//= style comments.
- Do not use comments like:
#+begin_src
if (foo)
/* Now that we know that foo is true we can call bar. */
bar ();
#+end_src
instead write the comment on the if line or before it. You may
also use a block and put the comment inside.
- Please use asterisks on the left of longer comments. This makes
it easier to read without syntax highlighting, on printouts, and
for blind people.
- Try to fit lines into 80 columns.
- Ignore signed/unsigned pointer mismatches
- No arithmetic on void pointers; cast to char* first.
- Do not use
#+begin_src
if ( 42 == foo )
#+end_src
this is harder to read and modern compilers are pretty good in
detecting accidental assignments. It is also suggested not to
compare to 0 or NULL but to test the value direct or with a '!';
this makes it easier to see that a boolean test is done.
- We use our own printf style functions like =es_printf=, and
=gpgrt_asprintf= (or the =es_asprintf= macro) which implement most
C99 features with the exception of =wchar_t= (which should anyway
not be used). Please use them always and do not resort to those
provided by libc. The rationale for using them is that we know
that the format specifiers work on all platforms and that we do
not need to chase platform dependent bugs. Note also that in
gnupg asprintf is a macro already evaluating to gpgrt_asprintf.
- It is common to have a label named "leave" for a function's
cleanup and return code. This helps with freeing memory and is a
convenient location to set a breakpoint for debugging.
- Always use xfree() instead of free(). If it is not easy to see
that the freed variable is not anymore used, explicitly set the
variable to NULL.
- New code shall in general use xtrymalloc or xtrycalloc and check
for an error (use gpg_error_from_syserror()).
- Init function local variables only if needed so that the compiler
can do a better job in detecting uninitialized variables which may
indicate a problem with the code.
- Never init static or file local variables to 0 to make sure they
end up in BSS.
- Put extra parenthesis around terms with binary operators to make
it clear that the binary operator was indeed intended.
- Use --enable-maintainer-mode with configure so that all suitable
warnings are enabled.
** Variable names
Follow the GNU standards. Here are some conventions you may want to
stick to (do not rename existing "wrong" uses without a good reason).
- err :: This conveys an error code of type =gpg_error_t= which is
compatible to an =int=. To compare such a variable to a
GPG_ERR_ constant, it is necessary to access the value like
this: =gpg_err_code(err)=.
- ec :: This is used for a gpg-error code which has no source part
(=gpg_err_code_t=) and will eventually be used as input to
=gpg_err_make=.
- rc :: Used for all kind of other errors; for example system
calls. The value is not compatible with gpg-error.
*** C99 language features
In GnuPG 2.x, but *not in 1.4* and not in most libraries, a limited
set of C99 features may be used:
- Variadic macros:
: #define foo(a,...) bar(a, __VA_ARGS__)
- The predefined macro =__func__=:
: log_debug ("%s: Problem with foo\n", __func__);
Although we usually make use of the =u16=, =u32=, and =u64= types,
it is also possible to include =<stdint.h>= and use =int16_t=,
=int32_t=, =int64_t=, =uint16_t=, =uint32_t=, and =uint64_t=. But do
not use =int8_t= or =uint8_t=.
** Commit log keywords
- GnuPG-bug-id :: Values are comma or space delimited bug numbers
from bug.gnupg.org pertaining to this commit.
- Debian-bug-id :: Same as above but from the Debian bug tracker.
- CVE-id :: CVE id number pertaining to this commit.
- Regression-due-to :: Commit id of the regression fixed by this commit.
- Fixes-commit :: Commit id this commit fixes.
- Updates-commit :: Commit id this commit updates.
- See-commit :: Commit id of a related commit.
- Reported-by :: Value is a name or mail address of a bug reporte.
- Suggested-by :: Value is a name or mail address of someone how
suggested this change.
- Co-authored-by :: Name or mail address of a co-author
- Some-comments-by :: Name or mail address of the author of
additional comments (commit log or code).
- Proofread-by :: Sometimes used by translation commits.
- Signed-off-by :: Name or mail address of the developer.
- Backported-from-master :: Value is the commit id of the original patch.
- Ported-from-stable :: Value is the commit id of the original patch.
** Sending patches
Submitting patches, and subsequent discussions around them,
happens via the gnupg-devel@gnupg.org public mailing list.
Send your patches to that list, preferably PGP/MIME signed. Make sure
to include a mention of 'gnupg' (or gpgme, libassuan, etc) in the
subject line; the list is used for several different projects.
In general you should send patches only for the master branch; we may
later decide to backport to another branch. Please ask first before
sending patches for another branch.
If you're working from the Git repo, here's a suggested workflow:
- Configure git send-email defaults:
: git config format.subjectPrefix 'PATCH gnupg'
: git config sendemail.to gnupg-devel@gnupg.org
(For other sub-projects adjust accordingly)
- hack hack hack
- Commit your changes; group changes into easily-reviewable commit
units, feel free to submit several patches at once.
e.g. if you want to submit a single patch on top of master, do:
: git send-email --annotate -1
e.g. if you have two commits on top of master, do:
: git send-email --annotate --cover-letter -2
(that prompts you for a summary mail to precede your actual patch
mails)
- use Git's --dry-run option to test your setup
* Windows
** How to build an installer for Windows
Your best bet is to use a decent Debian System for development.
You need to install a long list of tools for building. This list
still needs to be compiled. However, the build process will stop
if a tool is missing. GNU make is required (on non GNU systems
often installed as "gmake"). The installer requires a couple of
extra software to be available either as tarballs or as local git
repositories. In case this file here is part of a gnupg-w32-2.*.xz
complete tarball as distributed from the same place as a binary
installer, all such tarballs are already included.
Cd to the GnuPG source directory and use one of one of these
command:
- If sources are included (gnupg-w32-*.tar.xz)
make -f build-aux/speedo.mk WHAT=this installer
- To build from tarballs
make -f build-aux/speedo.mk WHAT=release TARBALLS=TARDIR installer
- To build from local GIT repos
make -f build-aux/speedo.mk WHAT=git TARBALLS=TARDIR installer
Note that also you need to supply tarballs with supporting
libraries even if you build from git. The makefile expects only
the core GnuPG software to be available as local GIT repositories.
speedo.mk has the versions of the tarballs and the branch names of
the git repositories. In case of problems, don't hesitate to ask
on the gnupg-devel mailing for help.
* Debug hints
See the manual for some hints.
* Various information
** Directory Layout
- ./ :: Readme, configure
- ./agent :: Gpg-agent and related tools
- ./doc :: Documentation
- ./g10 :: Gpg program here called gpg2
- ./sm :: Gpgsm program
- ./jnlib :: Not used (formerly used utility functions)
- ./common :: Utility functions
- ./kbx :: Keybox library
- ./scd :: Smartcard daemon
- ./scripts :: Scripts needed by configure and others
- ./dirmngr :: The directory manager
** Detailed Roadmap
This list of files is not up to date!
- g10/gpg.c :: Main module with option parsing and all the stuff you
have to do on startup. Also has the exit handler and
some helper functions.
- g10/parse-packet.c ::
- g10/build-packet.c ::
- g10/free-packet.c :: Parsing and creating of OpenPGP message packets.
- g10/getkey.c :: Key selection code
- g10/pkclist.c :: Build a list of public keys
- g10/skclist.c :: Build a list of secret keys
- g10/keyring.c :: Keyring access functions
- g10/keydb.h ::
- g10/keyid.c :: Helper functions to get the keyid, fingerprint etc.
- g10/trustdb.c :: Web-of-Trust computations
- g10/trustdb.h ::
- g10/tdbdump.c :: Export/import/list the trustdb.gpg
- g10/tdbio.c :: I/O handling for the trustdb.gpg
- g10/tdbio.h ::
- g10/compress.c :: Filter to handle compression
- g10/filter.h :: Declarations for all filter functions
- g10/delkey.c :: Delete a key
- g10/kbnode.c :: Helper for the kbnode_t linked list
- g10/main.h :: Prototypes and some constants
- g10/mainproc.c :: Message processing
- g10/armor.c :: Ascii armor filter
- g10/mdfilter.c :: Filter to calculate hashes
- g10/textfilter.c :: Filter to handle CR/LF and trailing white space
- g10/cipher.c :: En-/Decryption filter
- g10/misc.c :: Utility functions
- g10/options.h :: Structure with all the command line options
and related constants
- g10/openfile.c :: Create/Open Files
- g10/keyserver.h :: Keyserver access dispatcher.
- g10/packet.h :: Definition of OpenPGP structures.
- g10/passphrase.c :: Passphrase handling code
- g10/pubkey-enc.c :: Process a public key encoded packet.
- g10/seckey-cert.c :: Not anymore used
- g10/seskey.c :: Make session keys etc.
- g10/import.c :: Import keys into our key storage.
- g10/export.c :: Export keys to the OpenPGP format.
- g10/sign.c :: Create signature and optionally encrypt.
- g10/plaintext.c :: Process plaintext packets.
- g10/decrypt-data.c :: Decrypt an encrypted data packet
- g10/encrypt.c :: Main encryption driver
- g10/revoke.c :: Create recovation certificates.
- g10/keylist.c :: Print information about OpenPGP keys
- g10/sig-check.c :: Check a signature
- g10/helptext.c :: Show online help texts
- g10/verify.c :: Verify signed data.
- g10/decrypt.c :: Decrypt and verify data.
- g10/keyedit.c :: Edit properties of a key.
- g10/dearmor.c :: Armor utility.
- g10/keygen.c :: Generate a key pair
** Memory allocation
Use only the functions:
- xmalloc
- xmalloc_secure
- xtrymalloc
- xtrymalloc_secure
- xcalloc
- xcalloc_secure
- xtrycalloc
- xtrycalloc_secure
- xrealloc
- xtryrealloc
- xstrdup
- xtrystrdup
- xfree
The *secure versions allocate memory in the secure memory. That is,
swapping out of this memory is avoided and is gets overwritten on
free. Use this for passphrases, session keys and other sensitive
material. This memory set aside for secure memory is linited to a few
k. In general the function don't print a memory message and
terminate the process if there is not enough memory available. The
"try" versions of the functions return NULL instead.
** Logging
TODO
** Option parsing
GnuPG does not use getopt or GNU getopt but functions of it's own.
See util/argparse.c for details. The advantage of these functions is
that it is more easy to display and maintain the help texts for the
options. The same option table is also used to parse resource files.
** What is an IOBUF
This is the data structure used for most I/O of gnupg. It is similar
to System V Streams but much simpler. Because OpenPGP messages are
nested in different ways; the use of such a system has big advantages.
Here is an example, how it works: If the parser sees a packet header
with a partial length, it pushes the block_filter onto the IOBUF to
handle these partial length packets: from now on you don't have to
worry about this. When it sees a compressed packet it pushes the
uncompress filter and the next read byte is one which has already been
uncompressed by this filter. Same goes for enciphered packet,
plaintext packets and so on. The file g10/encode.c might be a good
starting point to see how it is used - actually this is the other way:
constructing messages using pushed filters but it may be easier to
understand.
+
+** Notes on how to create test data
+
+On 2016-02-28 we created a lot of AEAD test data using a command
+similar to this:
+
+--8<---------------cut here---------------start------------->8---
+for algo in eax ocb; do
+ for csize in 6 7 12 13 14 30; do
+ for len in 0 $(seq 0 200) $(seq 8100 8200) $(seq 16350 16400) \
+ $(seq 20000 20100); do
+ awk </dev/null -v i=$len 'BEGIN{while(i){i--;printf"~"}}' \
+ | gpg --no-options -v --rfc4880bis --batch --passphrase "abc" \
+ --s2k-count 1025 --s2k-digest-algo sha256 -z0 \
+ --force-aead --aead-algo $algo --cipher aes -a \
+ --chunk-size $csize -c >symenc-aead-eax-c$csize-$len.asc
+ done
+ done
+done
+--8<---------------cut here---------------end--------------->8---
diff --git a/doc/gpgv.texi b/doc/gpgv.texi
index 159c8dddb..005ff422f 100644
--- a/doc/gpgv.texi
+++ b/doc/gpgv.texi
@@ -1,182 +1,187 @@
@c Copyright (C) 2004 Free Software Foundation, Inc.
@c This is part of the GnuPG manual.
@c For copying conditions, see the file GnuPG.texi.
@c
@c This is included by tools.texi.
@c
@include defs.inc
@manpage gpgv.1
@node gpgv
@section Verify OpenPGP signatures
@ifset manverb
.B gpgv
\- Verify OpenPGP signatures
@end ifset
@mansect synopsis
@ifset manverb
.B gpgv
.RI [ options ]
.I sigfile
.RI [ datafiles ]
@end ifset
@mansect description
@code{@gpgvname} is an OpenPGP signature verification tool.
This program is actually a stripped-down version of @code{gpg} which is
only able to check signatures. It is somewhat smaller than the fully-blown
@code{gpg} and uses a different (and simpler) way to check that
the public keys used to make the signature are valid. There are
no configuration files and only a few options are implemented.
@code{@gpgvname} assumes that all keys in the keyring are trustworthy.
That does also mean that it does not check for expired or revoked
keys.
If no @code{--keyring} option is given, @code{gpgv} looks for a
``default'' keyring named @file{trustedkeys.kbx} (preferred) or
@file{trustedkeys.gpg} in the home directory of GnuPG, either the
default home directory or the one set by the @code{--homedir} option
or the @code{GNUPGHOME} environment variable. If any @code{--keyring}
option is used, @code{gpgv} will not look for the default keyring. The
@code{--keyring} option may be used multiple times and all specified
keyrings will be used together.
@noindent
@mansect options
@code{@gpgvname} recognizes these options:
@table @gnupgtabopt
@item --verbose
@itemx -v
@opindex verbose
Gives more information during processing. If used
twice, the input data is listed in detail.
@item --quiet
@itemx -q
@opindex quiet
Try to be as quiet as possible.
@item --keyring @var{file}
@opindex keyring
Add @var{file} to the list of keyrings.
If @var{file} begins with a tilde and a slash, these
are replaced by the HOME directory. If the filename
does not contain a slash, it is assumed to be in the
home-directory ("~/.gnupg" if @option{--homedir} is not used).
@item --output @var{file}
@itemx -o @var{file}
@opindex output
Write output to @var{file}; to write to stdout use @code{-}. This
option can be used to get the signed text from a cleartext or binary
signature; it also works for detached signatures, but in that case
this option is in general not useful. Note that an existing file will
be overwritten.
@item --status-fd @var{n}
@opindex status-fd
Write special status strings to the file descriptor @var{n}. See the
file DETAILS in the documentation for a listing of them.
@item --logger-fd @code{n}
@opindex logger-fd
Write log output to file descriptor @code{n} and not to stderr.
@item --log-file @code{file}
@opindex log-file
Same as @option{--logger-fd}, except the logger data is written to
file @code{file}. Use @file{socket://} to log to socket.
@item --ignore-time-conflict
@opindex ignore-time-conflict
GnuPG normally checks that the timestamps associated with keys and
signatures have plausible values. However, sometimes a signature seems to
be older than the key due to clock problems. This option turns these
checks into warnings.
@include opt-homedir.texi
@item --weak-digest @code{name}
@opindex weak-digest
Treat the specified digest algorithm as weak. Signatures made over
weak digests algorithms are normally rejected. This option can be
supplied multiple times if multiple algorithms should be considered
weak. MD5 is always considered weak, and does not need to be listed
explicitly.
@item --enable-special-filenames
@opindex enable-special-filenames
This option enables a mode in which filenames of the form
@file{-&n}, where n is a non-negative decimal number,
refer to the file descriptor n and not to a file with that name.
@item --assert-pubkey-algo @var{algolist}
@opindex assert-pubkey-algo
This option works in the same way as described for @command{gpg}.
+@item --print-notation @var{name}
+@itemx -N @var{name}
+If the signature verifies print the notation @var{name} to stdout if
+it exists.
+
@end table
@mansect return value
The program returns 0 if everything is fine, 1 if at least
one signature was bad, and other error codes for fatal errors.
@mansect examples
@subsection Examples
@table @asis
@item @gpgvname @code{pgpfile}
@itemx @gpgvname @code{sigfile} [@code{datafile}]
Verify the signature of the file. The second form is used for detached
signatures, where @code{sigfile} is the detached signature (either
ASCII-armored or binary) and @code{datafile} contains the signed data;
if @code{datafile} is "-" the signed data is expected on
@code{stdin}; if @code{datafile} is not given the name of the file
holding the signed data is constructed by cutting off the extension
(".asc", ".sig" or ".sign") from @code{sigfile}.
@end table
@mansect environment
@subsection Environment
@table @asis
@item HOME
Used to locate the default home directory.
@item GNUPGHOME
If set directory used instead of "~/.gnupg".
@end table
@mansect files
@subsection FILES
Default keyring file is expected in the GnuPG home directory
(@pxref{option --homedir}, @code{GNUPGHOME}).
@table @file
@item ~/.gnupg/trustedkeys.kbx
@efindex trustedkeys.kbx
The default keyring with the allowed keys, using the new keybox format.
@item ~/.gnupg/trustedkeys.gpg
@efindex trustedkeys.gpg
When @file{trustedkeys.kbx} is not available, the default keyring with
the allowed keys, using a legacy format.
@end table
@mansect see also
@command{gpg}(1)
@include see-also-note.texi
diff --git a/g10/gpgv.c b/g10/gpgv.c
index 6d3d25f50..686fc8891 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -1,862 +1,869 @@
/* gpgv.c - The GnuPG signature verify utility
* Copyright (C) 1998-2020 Free Software Foundation, Inc.
* Copyright (C) 1997-2019 Werner Koch
* Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#ifdef HAVE_DOSISH_SYSTEM
#include <fcntl.h> /* for setmode() */
#endif
#ifdef HAVE_LIBREADLINE
#define GNUPG_LIBREADLINE_H_INCLUDED
#include <readline/readline.h>
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/iobuf.h"
#include "main.h"
#include "options.h"
#include "keydb.h"
#include "trustdb.h"
#include "filter.h"
#include "../common/ttyio.h"
#include "../common/i18n.h"
#include "../common/sysutils.h"
#include "../common/status.h"
#include "call-agent.h"
#include "../common/init.h"
enum cmd_and_opt_values {
aNull = 0,
oQuiet = 'q',
oVerbose = 'v',
oOutput = 'o',
+ oPrintNotation = 'N',
oBatch = 500,
oKeyring,
oIgnoreTimeConflict,
oStatusFD,
oLoggerFD,
oLoggerFile,
oHomedir,
oWeakDigest,
oEnableSpecialFilenames,
oDebug,
oAssertPubkeyAlgo,
aTest
};
static gpgrt_opt_t opts[] = {
ARGPARSE_group (300, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_s (oKeyring, "keyring",
N_("|FILE|take the keys from the keyring FILE")),
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict",
N_("make timestamp conflicts only a warning")),
ARGPARSE_s_i (oStatusFD, "status-fd",
N_("|FD|write status info to this FD")),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
ARGPARSE_s_s (oLoggerFile, "log-file", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oWeakDigest, "weak-digest",
N_("|ALGO|reject signatures made with ALGO")),
ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
+ ARGPARSE_s_s (oPrintNotation, "print-notation",
+ N_("|NAME|print the notation NAME to stdout")),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"),
ARGPARSE_end ()
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_PACKET_VALUE , "packet" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_FILTER_VALUE , "filter" },
{ DBG_IOBUF_VALUE , "iobuf" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_TRUST_VALUE , "trust" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
{ DBG_CLOCK_VALUE , "clock" },
{ DBG_LOOKUP_VALUE , "lookup" },
{ DBG_EXTPROG_VALUE, "extprog" },
{ 0, NULL }
};
int g10_errors_seen = 0;
int assert_signer_true = 0;
int assert_pubkey_algo_false = 0;
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
static const char *
my_strusage( int level )
{
static char *ver_gcry;
const char *p;
switch (level)
{
case 9: p = "GPL-3.0-or-later"; break;
case 11: p = "@GPG@v (GnuPG)";
break;
case 13: p = VERSION; break;
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 1:
case 40: p = _("Usage: gpgv [options] [files] (-h for help)");
break;
case 41: p = _("Syntax: gpgv [options] [files]\n"
"Check signatures against known trusted keys\n");
break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
default: p = NULL;
}
return p;
}
int
main( int argc, char **argv )
{
gpgrt_argparse_t pargs;
int rc=0;
strlist_t sl;
strlist_t nrings = NULL;
ctrl_t ctrl;
early_system_init ();
gpgrt_set_strusage (my_strusage);
log_set_prefix ("gpgv", GPGRT_LOG_WITH_PREFIX);
/* Make sure that our subsystems are ready. */
i18n_init();
init_common_subsystems (&argc, &argv);
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
gnupg_init_signals (0, NULL);
opt.command_fd = -1; /* no command fd */
opt.keyserver_options.options |= KEYSERVER_AUTO_KEY_RETRIEVE;
opt.trust_model = TM_ALWAYS;
opt.no_sig_cache = 1;
opt.flags.require_cross_cert = 1;
opt.batch = 1;
opt.answer_yes = 1;
opt.weak_digests = NULL;
tty_no_terminal(1);
tty_batchmode(1);
dotlock_disable ();
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
additional_weak_digest("MD5");
gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPG);
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= ARGPARSE_FLAG_KEEP;
while (gpgrt_argparser (&pargs, opts, NULL))
{
switch (pargs.r_opt)
{
case ARGPARSE_CONFFILE: break;
case oQuiet: opt.quiet = 1; break;
case oVerbose:
opt.verbose++;
opt.list_sigs=1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
break;
case oDebug:
if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
{
pargs.r_opt = ARGPARSE_INVALID_ARG;
pargs.err = ARGPARSE_PRINT_ERROR;
}
break;
case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
case oOutput: opt.outfile = pargs.r.ret_str; break;
case oStatusFD:
set_status_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oLoggerFD:
log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oLoggerFile:
log_set_file (pargs.r.ret_str);
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
| GPGRT_LOG_WITH_TIME
| GPGRT_LOG_WITH_PID) );
break;
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oWeakDigest:
additional_weak_digest(pargs.r.ret_str);
break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oEnableSpecialFilenames:
enable_special_filenames ();
break;
case oAssertPubkeyAlgo:
if (!opt.assert_pubkey_algos)
opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str);
else
{
char *tmp = opt.assert_pubkey_algos;
opt.assert_pubkey_algos = xstrconcat (tmp, ",",
pargs.r.ret_str, NULL);
xfree (tmp);
}
break;
+ case oPrintNotation:
+ append_to_strlist (&opt.print_notations, pargs.r.ret_str);
+ break;
+
default : pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
gpgrt_argparse (NULL, &pargs, NULL); /* Release internal state. */
if (log_get_errorcount (0))
g10_exit(2);
if (opt.verbose > 1)
set_packet_list_mode(1);
/* Note: We open all keyrings in read-only mode. */
if (!nrings) /* No keyring given: use default one. */
keydb_add_resource ("trustedkeys" EXTSEP_S "kbx",
(KEYDB_RESOURCE_FLAG_READONLY
|KEYDB_RESOURCE_FLAG_GPGVDEF));
for (sl = nrings; sl; sl = sl->next)
keydb_add_resource (sl->d, KEYDB_RESOURCE_FLAG_READONLY);
FREE_STRLIST (nrings);
ctrl = xcalloc (1, sizeof *ctrl);
if ((rc = verify_signatures (ctrl, argc, argv)))
log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
keydb_release (ctrl->cached_getkey_kdb);
xfree (ctrl);
/* cleanup */
g10_exit (0);
return 8; /*NOTREACHED*/
}
void
g10_exit (int rc)
{
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_pubkey_algos && assert_pubkey_algo_false)
rc = 1;
exit (rc);
}
/* Stub:
* We have to override the trustcheck from pkclist.c because
* this utility assumes that all keys in the keyring are trustworthy
*/
gpg_error_t
check_signatures_trust (ctrl_t ctrl, kbnode_t kblock,
PKT_public_key *pk, PKT_signature *sig)
{
(void)ctrl;
(void)kblock;
(void)pk;
(void)sig;
return 0;
}
void
read_trust_options (ctrl_t ctrl,
byte *trust_model, ulong *created, ulong *nextcheck,
byte *marginals, byte *completes, byte *cert_depth,
byte *min_cert_level)
{
(void)ctrl;
(void)trust_model;
(void)created;
(void)nextcheck;
(void)marginals;
(void)completes;
(void)cert_depth;
(void)min_cert_level;
}
/* Stub:
* We don't have the trustdb , so we have to provide some stub functions
* instead
*/
int
cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk)
{
(void)ctrl;
(void)pk;
return 0;
}
void
check_trustdb_stale (ctrl_t ctrl)
{
(void)ctrl;
}
int
get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk,
PKT_user_id *uid)
{
(void)ctrl;
(void)kb;
(void)pk;
(void)uid;
return '?';
}
unsigned int
get_validity (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask)
{
(void)ctrl;
(void)kb;
(void)pk;
(void)uid;
(void)sig;
(void)may_ask;
return 0;
}
const char *
trust_value_to_string (unsigned int value)
{
(void)value;
return "err";
}
const char *
uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid)
{
(void)ctrl;
(void)key;
(void)uid;
return "err";
}
int
get_ownertrust_info (ctrl_t ctrl, PKT_public_key *pk, int no_create)
{
(void)ctrl;
(void)pk;
(void)no_create;
return '?';
}
unsigned int
get_ownertrust (ctrl_t ctrl, PKT_public_key *pk)
{
(void)ctrl;
(void)pk;
return TRUST_UNKNOWN;
}
const char *
get_ownertrust_string (ctrl_t ctrl, PKT_public_key *pk, int no_create)
{
(void)ctrl;
(void)pk;
(void)no_create;
return "";
}
/* Stubs:
* Because we only work with trusted keys, it does not make sense to
* get them from a keyserver
*/
struct keyserver_spec *
keyserver_match (struct keyserver_spec *spec)
{
(void)spec;
return NULL;
}
int
keyserver_any_configured (ctrl_t ctrl)
{
(void)ctrl;
return 0;
}
int
keyserver_import_keyid (u32 *keyid, void *dummy, unsigned int flags)
{
(void)keyid;
(void)dummy;
(void)flags;
return -1;
}
int
keyserver_import_fpr (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
struct keyserver_spec *keyserver, unsigned int flags)
{
(void)ctrl;
(void)fprint;
(void)fprint_len;
(void)keyserver;
(void)flags;
return -1;
}
int
keyserver_import_fpr_ntds (ctrl_t ctrl,
const byte *fprint, size_t fprint_len)
{
(void)ctrl;
(void)fprint;
(void)fprint_len;
return -1;
}
int
keyserver_import_cert (const char *name)
{
(void)name;
return -1;
}
gpg_error_t
keyserver_import_wkd (ctrl_t ctrl, const char *name, unsigned int flags,
unsigned char **fpr, size_t *fpr_len)
{
(void)ctrl;
(void)name;
(void)flags;
(void)fpr;
(void)fpr_len;
return GPG_ERR_BUG;
}
int
keyserver_import_mbox (const char *name,struct keyserver_spec *spec)
{
(void)name;
(void)spec;
return -1;
}
int
keyserver_import_ntds (ctrl_t ctrl, const char *mbox,
unsigned char **fpr, size_t *fprlen)
{
(void)ctrl;
(void)mbox;
(void)fpr;
(void)fprlen;
return -1;
}
int
keyserver_import_ldap (const char *name)
{
(void)name;
return -1;
}
gpg_error_t
read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname,
const void *buffer, size_t buflen,
kbnode_t *r_keyblock)
{
(void)ctrl;
(void)fname;
(void)buffer;
(void)buflen;
(void)r_keyblock;
return -1;
}
gpg_error_t
import_included_key_block (ctrl_t ctrl, kbnode_t keyblock)
{
(void)ctrl;
(void)keyblock;
return -1;
}
/* Stub:
* No encryption here but mainproc links to these functions.
*/
gpg_error_t
get_session_key (ctrl_t ctrl, struct pubkey_enc_list *k, DEK *dek)
{
(void)ctrl;
(void)k;
(void)dek;
return GPG_ERR_GENERAL;
}
/* Stub: */
gpg_error_t
get_override_session_key (DEK *dek, const char *string)
{
(void)dek;
(void)string;
return GPG_ERR_GENERAL;
}
/* Stub: */
int
decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek,
int *compliance_error)
{
(void)ctrl;
(void)procctx;
(void)ed;
(void)dek;
(void)compliance_error;
return GPG_ERR_GENERAL;
}
/* Stub:
* No interactive commands, so we don't need the helptexts
*/
void
display_online_help (const char *keyword)
{
(void)keyword;
}
/* Stub:
* We don't use secret keys, but getkey.c links to this
*/
int
check_secret_key (PKT_public_key *pk, int n)
{
(void)pk;
(void)n;
return GPG_ERR_GENERAL;
}
/* Stub:
* No secret key, so no passphrase needed
*/
DEK *
passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache,
const char *tmp, unsigned int flags, int *canceled)
{
(void)cipher_algo;
(void)s2k;
(void)create;
(void)nocache;
(void)tmp;
(void)flags;
if (canceled)
*canceled = 0;
return NULL;
}
void
passphrase_clear_cache (const char *cacheid)
{
(void)cacheid;
}
struct keyserver_spec *
parse_preferred_keyserver(PKT_signature *sig)
{
(void)sig;
return NULL;
}
struct keyserver_spec *
parse_keyserver_uri (const char *uri, int require_scheme,
const char *configname, unsigned int configlineno)
{
(void)uri;
(void)require_scheme;
(void)configname;
(void)configlineno;
return NULL;
}
void
free_keyserver_spec (struct keyserver_spec *keyserver)
{
(void)keyserver;
}
/* Stubs to avoid linking to photoid.c */
void
show_photos (const struct user_attribute *attrs, int count, PKT_public_key *pk)
{
(void)attrs;
(void)count;
(void)pk;
}
int
parse_image_header (const struct user_attribute *attr, byte *type, u32 *len)
{
(void)attr;
(void)type;
(void)len;
return 0;
}
char *
image_type_to_string (byte type, int string)
{
(void)type;
(void)string;
return NULL;
}
#ifdef ENABLE_CARD_SUPPORT
int
agent_scd_getattr (const char *name, struct agent_card_info_s *info)
{
(void)name;
(void)info;
return 0;
}
#endif /* ENABLE_CARD_SUPPORT */
/* We do not do any locking, so use these stubs here */
void
dotlock_disable (void)
{
}
dotlock_t
dotlock_create (const char *file_to_lock, unsigned int flags)
{
(void)file_to_lock;
(void)flags;
return NULL;
}
void
dotlock_destroy (dotlock_t h)
{
(void)h;
}
int
dotlock_take (dotlock_t h, long timeout)
{
(void)h;
(void)timeout;
return 0;
}
int
dotlock_release (dotlock_t h)
{
(void)h;
return 0;
}
void
dotlock_remove_lockfiles (void)
{
}
int
agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk)
{
(void)ctrl;
(void)pk;
return 0;
}
gpg_error_t
agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock)
{
(void)ctrl;
(void)keyblock;
return gpg_error (GPG_ERR_NO_SECKEY);
}
gpg_error_t
agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
char **r_serialno, int *r_cleartext)
{
(void)ctrl;
(void)hexkeygrip;
(void)r_cleartext;
*r_serialno = NULL;
return gpg_error (GPG_ERR_NO_SECKEY);
}
gpg_error_t
export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
const void *prefix, size_t prefixlen,
export_stats_t stats,
kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
{
(void)ctrl;
(void)keyspec;
(void)options;
(void)prefix;
(void)prefixlen;
(void)stats;
*r_keyblock = NULL;
*r_data = NULL;
*r_datalen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
gpg_error_t
tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
PKT_public_key *pk, const char *user_id)
{
(void)ctrl;
(void)fp;
(void)pk;
(void)user_id;
return gpg_error (GPG_ERR_GENERAL);
}
gpg_error_t
tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
(void)ctrl;
(void)pk;
(void)user_id;
(void)policy;
return gpg_error (GPG_ERR_GENERAL);
}
const char *
tofu_policy_str (enum tofu_policy policy)
{
(void)policy;
return "unknown";
}
void
tofu_begin_batch_update (ctrl_t ctrl)
{
(void)ctrl;
}
void
tofu_end_batch_update (ctrl_t ctrl)
{
(void)ctrl;
}
gpg_error_t
tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
{
(void) ctrl;
(void) kb;
return 0;
}
const char *
revocation_reason_code_to_str (int code, char **freeme)
{
(void)code;
*freeme = NULL;
return "";
}
int
get_revocation_reason (PKT_signature *sig, char **r_reason,
char **r_comment, size_t *r_commentlen)
{
(void)sig;
(void)r_commentlen;
if (r_reason)
*r_reason = NULL;
if (r_comment)
*r_comment = NULL;
return 0;
}
const char *
impex_filter_getval (void *cookie, const char *propname)
{
(void)cookie;
(void)propname;
return NULL;
}
diff --git a/g10/keylist.c b/g10/keylist.c
index 1c531126f..42e9f65f5 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -1,2859 +1,2894 @@
/* keylist.c - Print information about OpenPGP keys
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2008, 2010, 2012 Free Software Foundation, Inc.
* Copyright (C) 2013, 2014 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_DOSISH_SYSTEM
# include <fcntl.h> /* for setmode() */
#endif
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "keydb.h"
#include "photoid.h"
#include "../common/util.h"
#include "../common/ttyio.h"
#include "trustdb.h"
#include "main.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "call-agent.h"
#include "../common/mbox-util.h"
#include "../common/zb32.h"
#include "tofu.h"
#include "../common/init.h"
#include "../common/recsel.h"
#include "../common/compliance.h"
#include "../common/pkscreening.h"
static void list_all (ctrl_t, int, int);
static void list_one (ctrl_t ctrl,
strlist_t names, int secret, int mark_secret);
static void locate_one (ctrl_t ctrl, strlist_t names, int no_local);
static void print_card_serialno (const char *serialno);
struct keylist_context
{
int check_sigs; /* If set signatures shall be verified. */
int good_sigs; /* Counter used if CHECK_SIGS is set. */
int inv_sigs; /* Counter used if CHECK_SIGS is set. */
int no_key; /* Counter used if CHECK_SIGS is set. */
int oth_err; /* Counter used if CHECK_SIGS is set. */
int no_validity; /* Do not show validity. */
};
/* An object and a global instance to store selectors created from
* --list-filter select=EXPR.
*/
struct list_filter_s
{
recsel_expr_t selkey;
};
struct list_filter_s list_filter;
/* The stream used to write attribute packets to. */
static estream_t attrib_fp;
static gpg_error_t list_keyblock (ctrl_t ctrl,
kbnode_t keyblock, int secret, int has_secret,
int fpr, struct keylist_context *listctx);
/* Release resources from a keylist context. */
static void
keylist_context_release (struct keylist_context *listctx)
{
(void)listctx; /* Nothing to release. */
}
static void
release_list_filter (struct list_filter_s *filt)
{
recsel_release (filt->selkey);
filt->selkey = NULL;
}
static void
cleanup_keylist_globals (void)
{
release_list_filter (&list_filter);
}
/* Parse and set an list filter from string. STRING has the format
* "NAME=EXPR" with NAME being the name of the filter. Spaces before
* and after NAME are not allowed. If this function is all called
* several times all expressions for the same NAME are concatenated.
* Supported filter names are:
*
* - select :: If the expression evaluates to true for a certain key
* this key will be listed. The expression may use any
* variable defined for the export and import filters.
*
*/
gpg_error_t
parse_and_set_list_filter (const char *string)
{
gpg_error_t err;
/* Auto register the cleanup function. */
register_mem_cleanup_func (cleanup_keylist_globals);
if (!strncmp (string, "select=", 7))
err = recsel_parse_expr (&list_filter.selkey, string+7);
else
err = gpg_error (GPG_ERR_INV_NAME);
if (!err && DBG_RECSEL)
recsel_dump (list_filter.selkey);
return err;
}
/* List the keys. If list is NULL, all available keys are listed.
* With LOCATE_MODE set the locate algorithm is used to find a key; if
* in addition NO_LOCAL is set the locate does not look into the local
* keyring. */
void
public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local)
{
#ifndef NO_TRUST_MODELS
if (opt.with_colons)
{
byte trust_model, marginals, completes, cert_depth, min_cert_level;
ulong created, nextcheck;
read_trust_options (ctrl, &trust_model, &created, &nextcheck,
&marginals, &completes, &cert_depth, &min_cert_level);
es_fprintf (es_stdout, "tru:");
if (nextcheck && nextcheck <= make_timestamp ())
es_fprintf (es_stdout, "o");
if (trust_model != opt.trust_model)
es_fprintf (es_stdout, "t");
if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
|| opt.trust_model == TM_TOFU_PGP)
{
if (marginals != opt.marginals_needed)
es_fprintf (es_stdout, "m");
if (completes != opt.completes_needed)
es_fprintf (es_stdout, "c");
if (cert_depth != opt.max_cert_depth)
es_fprintf (es_stdout, "d");
if (min_cert_level != opt.min_cert_level)
es_fprintf (es_stdout, "l");
}
es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck);
/* Only show marginals, completes, and cert_depth in the classic
or PGP trust models since they are not meaningful
otherwise. */
if (trust_model == TM_PGP || trust_model == TM_CLASSIC)
es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth);
es_fprintf (es_stdout, "\n");
}
#endif /*!NO_TRUST_MODELS*/
/* We need to do the stale check right here because it might need to
update the keyring while we already have the keyring open. This
is very bad for W32 because of a sharing violation. For real OSes
it might lead to false results if we are later listing a keyring
which is associated with the inode of a deleted file. */
check_trustdb_stale (ctrl);
#ifdef USE_TOFU
tofu_begin_batch_update (ctrl);
#endif
if (locate_mode)
locate_one (ctrl, list, no_local);
else if (!list)
list_all (ctrl, 0, opt.with_secret);
else
list_one (ctrl, list, 0, opt.with_secret);
#ifdef USE_TOFU
tofu_end_batch_update (ctrl);
#endif
}
void
secret_key_list (ctrl_t ctrl, strlist_t list)
{
(void)ctrl;
check_trustdb_stale (ctrl);
if (!list)
list_all (ctrl, 1, 0);
else /* List by user id */
list_one (ctrl, list, 1, 0);
}
/* Helper for print_key_info and print_key_info_log. */
static char *
format_key_info (ctrl_t ctrl, PKT_public_key *pk, int secret)
{
u32 keyid[2];
char *p;
char pkstrbuf[PUBKEY_STRING_SIZE];
char *result;
keyid_from_pk (pk, keyid);
/* If the pk was chosen by a particular user ID, that is the one to
print. */
if (pk->user_id)
p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0);
else
p = get_user_id_native (ctrl, keyid);
result = xtryasprintf ("%s %s/%s %s %s",
secret? (pk->flags.primary? "sec":"ssb")
/* */ : (pk->flags.primary? "pub":"sub"),
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr (keyid), datestr_from_pk (pk), p);
xfree (p);
return result;
}
/* Print basic information about a public or secret key. With FP
* passed as NULL, the tty output interface is used, otherwise output
* is directed to the given stream. INDENT gives the requested
* indentation; if that is a negative value indentation is suppressed
* for the first line. SECRET tells that the PK has a secret part.
* FIXME: This is similar in use to print_key_line and thus both
* functions should eventually be united.
*/
void
print_key_info (ctrl_t ctrl, estream_t fp,
int indent, PKT_public_key *pk, int secret)
{
int indentabs = indent >= 0? indent : -indent;
char *info;
/* Note: Negative values for INDENT are not yet needed. */
info = format_key_info (ctrl, pk, secret);
if (!fp && indent >= 0)
tty_printf ("\n"); /* (Backward compatibility to old code) */
tty_fprintf (fp, "%*s%s\n", indentabs, "",
info? info : "[Ooops - out of core]");
xfree (info);
}
/* Same as print_key_info put print using the log functions at
* LOGLEVEL. */
void
print_key_info_log (ctrl_t ctrl, int loglevel,
int indent, PKT_public_key *pk, int secret)
{
int indentabs = indent >= 0? indent : -indent;
char *info;
info = format_key_info (ctrl, pk, secret);
log_log (loglevel, "%*s%s\n", indentabs, "",
info? info : "[Ooops - out of core]");
xfree (info);
}
/* Print basic information of a secret key including the card serial
number information. */
#ifdef ENABLE_CARD_SUPPORT
void
print_card_key_info (estream_t fp, kbnode_t keyblock)
{
kbnode_t node;
char *hexgrip;
char *serialno;
int s2k_char;
char pkstrbuf[PUBKEY_STRING_SIZE];
int indent;
for (node = keyblock; node; node = node->next)
{
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
int rc;
PKT_public_key *pk = node->pkt->pkt.public_key;
serialno = NULL;
rc = hexkeygrip_from_pk (pk, &hexgrip);
if (rc)
{
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
s2k_char = '?';
}
else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
s2k_char = serialno? '>':' ';
else
s2k_char = '#'; /* Key not found. */
tty_fprintf (fp, "%s%c %s/%s %n",
node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb",
s2k_char,
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr_from_pk (pk),
&indent);
tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk));
tty_fprintf (fp, " ");
tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
if (serialno)
{
tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: "));
if (strlen (serialno) == 32
&& !strncmp (serialno, "D27600012401", 12))
{
/* This is an OpenPGP card. Print the relevant part. */
/* Example: D2760001240101010001000003470000 */
/* xxxxyyyyyyyy */
tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20);
}
else
tty_fprintf (fp, "%s", serialno);
}
tty_fprintf (fp, "\n");
xfree (hexgrip);
xfree (serialno);
}
}
}
#endif /*ENABLE_CARD_SUPPORT*/
/* Print the preferences line. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info
*/
void
show_preferences (PKT_user_id *uid, int indent, int mode, int verbose)
{
estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
const prefitem_t fake = { 0, 0 };
const prefitem_t *prefs;
int i;
if (!uid)
return;
if (uid->prefs)
prefs = uid->prefs;
else if (verbose)
prefs = &fake;
else
return;
if (verbose)
{
int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0;
tty_fprintf (fp, "%*s %s", indent, "", _("Cipher: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_SYM)
{
if (any)
tty_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (!openpgp_cipher_test_algo (prefs[i].value)
&& prefs[i].value < 100)
tty_fprintf (fp, "%s", openpgp_cipher_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == CIPHER_ALGO_3DES)
des_seen = 1;
}
}
if (!des_seen)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES));
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("AEAD: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_AEAD)
{
if (any)
tty_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (!openpgp_aead_test_algo (prefs[i].value)
&& prefs[i].value < 100)
tty_fprintf (fp, "%s", openpgp_aead_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
}
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("Digest: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_HASH)
{
if (any)
tty_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100)
tty_fprintf (fp, "%s", gcry_md_algo_name (prefs[i].value));
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == DIGEST_ALGO_SHA1)
sha1_seen = 1;
}
}
if (!sha1_seen)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "%s", gcry_md_algo_name (DIGEST_ALGO_SHA1));
}
tty_fprintf (fp, "\n%*s %s", indent, "", _("Compression: "));
for (i = any = 0; prefs[i].type; i++)
{
if (prefs[i].type == PREFTYPE_ZIP)
{
const char *s = compress_algo_to_string (prefs[i].value);
if (any)
tty_fprintf (fp, ", ");
any = 1;
/* We don't want to display strings for experimental algos */
if (s && prefs[i].value < 100)
tty_fprintf (fp, "%s", s);
else
tty_fprintf (fp, "[%d]", prefs[i].value);
if (prefs[i].value == COMPRESS_ALGO_NONE)
uncomp_seen = 1;
}
}
if (!uncomp_seen)
{
if (any)
tty_fprintf (fp, ", ");
else
{
tty_fprintf (fp, "%s",
compress_algo_to_string (COMPRESS_ALGO_ZIP));
tty_fprintf (fp, ", ");
}
tty_fprintf (fp, "%s", compress_algo_to_string (COMPRESS_ALGO_NONE));
}
if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify)
{
tty_fprintf (fp, "\n%*s %s", indent, "", _("Features: "));
any = 0;
if (uid->flags.mdc)
{
tty_fprintf (fp, "MDC");
any = 1;
}
if (uid->flags.aead)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, "AEAD");
}
if (!uid->flags.ks_modify)
{
if (any)
tty_fprintf (fp, ", ");
tty_fprintf (fp, _("Keyserver no-modify"));
}
}
tty_fprintf (fp, "\n");
}
else
{
tty_fprintf (fp, "%*s", indent, "");
for (i = 0; prefs[i].type; i++)
{
tty_fprintf (fp, " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' :
prefs[i].type == PREFTYPE_AEAD ? 'A' :
prefs[i].type == PREFTYPE_HASH ? 'H' :
prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?',
prefs[i].value);
}
if (uid->flags.mdc)
tty_fprintf (fp, " [mdc]");
if (uid->flags.aead)
tty_fprintf (fp, " [aead]");
if (!uid->flags.ks_modify)
tty_fprintf (fp, " [no-ks-modify]");
tty_fprintf (fp, "\n");
}
}
/* Flags = 0x01 hashed 0x02 critical. */
static void
status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
const byte * buf)
{
char status[40];
/* Don't print these. */
if (len > 256)
return;
snprintf (status, sizeof status,
"%d %u %u ", type, flags, (unsigned int) len);
write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0);
}
/* Print a policy URL. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*/
void
show_policy_url (PKT_signature * sig, int indent, int mode)
{
const byte *p;
size_t len;
int seq = 0, crit;
estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_POLICY, &len, &seq, &crit)))
{
if (mode != 2)
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (crit)
str = _("Critical signature policy: ");
else
str = _("Signature policy: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
tty_print_utf8_string2 (fp, p, len, 0);
tty_fprintf (fp, "\n");
}
if (mode > 0)
write_status_buffer (STATUS_POLICY_URL, p, len, 0);
}
}
/* Print a keyserver URL. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*/
void
show_keyserver_url (PKT_signature * sig, int indent, int mode)
{
const byte *p;
size_t len;
int seq = 0, crit;
estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &len, &seq, &crit)))
{
if (mode != 2)
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (crit)
str = _("Critical preferred keyserver: ");
else
str = _("Preferred keyserver: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
tty_print_utf8_string2 (fp, p, len, 0);
tty_fprintf (fp, "\n");
}
if (mode > 0)
status_one_subpacket (SIGSUBPKT_PREF_KS, len,
(crit ? 0x02 : 0) | 0x01, p);
}
}
/* Print notation data. Allowed values for MODE are:
* -1 - print to the TTY
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*
* Defined bits in WHICH:
* 1 - standard notations
* 2 - user notations
* 4 - print notations normally hidden
*/
void
show_notation (PKT_signature * sig, int indent, int mode, int which)
{
estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
notation_t nd, notations;
if (which == 0)
which = 3;
notations = sig_to_notation (sig);
/* There may be multiple notations in the same sig. */
for (nd = notations; nd; nd = nd->next)
{
if (!(which & 4) && !strcmp (nd->name, "manu"))
continue;
if (mode != 2)
{
int has_at = !!strchr (nd->name, '@');
if ((which & 1 && !has_at) || (which & 2 && has_at))
{
const char *str;
tty_fprintf (fp, "%*s", indent, "");
if (nd->flags.critical)
str = _("Critical signature notation: ");
else
str = _("Signature notation: ");
if (mode > 0)
log_info ("%s", str);
else
tty_fprintf (fp, "%s", str);
/* This is all UTF8 */
tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0);
tty_fprintf (fp, "=");
tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0);
/* (We need to use log_printf so that the next call to a
log function does not insert an extra LF.) */
if (mode > 0)
log_printf ("\n");
else
tty_fprintf (fp, "\n");
}
}
if (mode > 0)
{
write_status_buffer (STATUS_NOTATION_NAME,
nd->name, strlen (nd->name), 0);
if (nd->flags.critical || nd->flags.human)
write_status_text (STATUS_NOTATION_FLAGS,
nd->flags.critical && nd->flags.human? "1 1" :
nd->flags.critical? "1 0" : "0 1");
if (!nd->flags.human && nd->bdat && nd->blen)
write_status_buffer (STATUS_NOTATION_DATA,
nd->bdat, nd->blen, 250);
else
write_status_buffer (STATUS_NOTATION_DATA,
nd->value, strlen (nd->value), 50);
}
}
free_notation (notations);
}
+/* Output all the notation data in SIG matching a name given by
+ * --print-notation to stdout. */
+void
+print_matching_notations (PKT_signature *sig)
+{
+ notation_t nd, notations;
+ strlist_t sl;
+ const char *s;
+
+ if (!opt.print_notations)
+ return;
+
+ notations = sig_to_notation (sig);
+ for (nd = notations; nd; nd = nd->next)
+ {
+ for (sl=opt.print_notations; sl; sl = sl->next)
+ if (!strcmp (sl->d, nd->name))
+ break;
+ if (!sl || !*nd->value)
+ continue;
+ es_fprintf (es_stdout, "%s: ", nd->name);
+ for (s = nd->value; *s; s++)
+ {
+ if (*s == '\n')
+ es_fprintf (es_stdout, "\n%*s", (int)strlen (nd->name)+2, "");
+ else if (*s >= ' ' || *s != '\t')
+ es_putc (*s, es_stdout);
+ }
+ es_putc ('\n', es_stdout);
+ }
+
+ free_notation (notations);
+}
+
+
static void
print_signature_stats (struct keylist_context *s)
{
if (!s->check_sigs)
return; /* Signature checking was not requested. */
/* Better flush stdout so that the stats are always printed after
* the output. */
es_fflush (es_stdout);
if (s->good_sigs)
log_info (ngettext("%d good signature\n",
"%d good signatures\n", s->good_sigs), s->good_sigs);
if (s->inv_sigs)
log_info (ngettext("%d bad signature\n",
"%d bad signatures\n", s->inv_sigs), s->inv_sigs);
if (s->no_key)
log_info (ngettext("%d signature not checked due to a missing key\n",
"%d signatures not checked due to missing keys\n",
s->no_key), s->no_key);
if (s->oth_err)
log_info (ngettext("%d signature not checked due to an error\n",
"%d signatures not checked due to errors\n",
s->oth_err), s->oth_err);
}
/* List all keys. If SECRET is true only secret keys are listed. If
MARK_SECRET is true secret keys are indicated in a public key
listing. */
static void
list_all (ctrl_t ctrl, int secret, int mark_secret)
{
KEYDB_HANDLE hd;
KBNODE keyblock = NULL;
int rc = 0;
int any_secret;
const char *lastresname, *resname;
struct keylist_context listctx;
gpg_error_t listerr = 0;
memset (&listctx, 0, sizeof (listctx));
if (opt.check_sigs)
listctx.check_sigs = 1;
hd = keydb_new (ctrl);
if (!hd)
rc = gpg_error_from_syserror ();
else
rc = keydb_search_first (hd);
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc));
goto leave;
}
lastresname = NULL;
do
{
if (secret)
glo_ctrl.silence_parse_warnings++;
rc = keydb_get_keyblock (hd, &keyblock);
if (secret)
glo_ctrl.silence_parse_warnings--;
if (rc)
{
if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY)
continue; /* Skip legacy keys. */
log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (secret || mark_secret)
any_secret = !agent_probe_any_secret_key (ctrl, keyblock);
else
any_secret = 0;
if (secret && !any_secret)
; /* Secret key listing requested but this isn't one. */
else
{
if (!opt.with_colons && !(opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
{
resname = keydb_get_resource_name (hd);
if (lastresname != resname)
{
int i;
es_fprintf (es_stdout, "%s\n", resname);
for (i = strlen (resname); i; i--)
es_putc ('-', es_stdout);
es_putc ('\n', es_stdout);
lastresname = resname;
}
}
merge_keys_and_selfsig (ctrl, keyblock);
listerr = list_keyblock (ctrl, keyblock, secret, any_secret,
opt.fingerprint, &listctx);
}
release_kbnode (keyblock);
keyblock = NULL;
}
while (!listerr && !(rc = keydb_search_next (hd)));
es_fflush (es_stdout);
if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
if (keydb_get_skipped_counter (hd))
log_info (ngettext("Warning: %lu key skipped due to its large size\n",
"Warning: %lu keys skipped due to their large sizes\n",
keydb_get_skipped_counter (hd)),
keydb_get_skipped_counter (hd));
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&listctx);
leave:
keylist_context_release (&listctx);
release_kbnode (keyblock);
keydb_release (hd);
}
static void
list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret)
{
int rc = 0;
KBNODE keyblock = NULL;
GETKEY_CTX ctx;
int any_secret;
const char *resname;
const char *keyring_str = _("Keyring");
int i;
struct keylist_context listctx;
gpg_error_t listerr = 0;
memset (&listctx, 0, sizeof (listctx));
if (!secret && opt.check_sigs)
listctx.check_sigs = 1;
/* fixme: using the bynames function has the disadvantage that we
* don't know whether one of the names given was not found. OTOH,
* this function has the advantage to list the names in the
* sequence as defined by the keyDB and does not duplicate
* outputs. A solution could be do test whether all given have
* been listed (this needs a way to use the keyDB search
* functions) or to have the search function return indicators for
* found names. Yet another way is to use the keydb search
* facilities directly. */
rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock);
if (rc)
{
log_error ("error reading key: %s\n", gpg_strerror (rc));
getkey_end (ctrl, ctx);
write_status_error ("keylist.getkey", rc);
return;
}
do
{
/* getkey_bynames makes sure that only secret keys are returned
* if requested, thus we do not need to test again. With
* MARK_SECRET set (ie. option --with-secret) we have to test
* for a secret key, though. */
if (secret)
any_secret = 1;
else if (mark_secret)
any_secret = !agent_probe_any_secret_key (ctrl, keyblock);
else
any_secret = 0;
if (secret && !any_secret)
;/* Secret key listing requested but getkey_bynames failed. */
else
{
if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons)
{
resname = keydb_get_resource_name (get_ctx_handle (ctx));
es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname);
for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--)
es_putc ('-', es_stdout);
es_putc ('\n', es_stdout);
}
listerr = list_keyblock (ctrl, keyblock, secret, any_secret,
opt.fingerprint, &listctx);
}
release_kbnode (keyblock);
}
while (!listerr && !getkey_next (ctrl, ctx, NULL, &keyblock));
getkey_end (ctrl, ctx);
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&listctx);
keylist_context_release (&listctx);
}
static void
locate_one (ctrl_t ctrl, strlist_t names, int no_local)
{
int rc = 0;
strlist_t sl;
GETKEY_CTX ctx = NULL;
KBNODE keyblock = NULL;
struct keylist_context listctx;
gpg_error_t listerr = 0;
memset (&listctx, 0, sizeof (listctx));
if (opt.check_sigs)
listctx.check_sigs = 1;
for (sl = names; sl && !listerr; sl = sl->next)
{
rc = get_best_pubkey_byname (ctrl,
no_local? GET_PUBKEY_NO_LOCAL
/* */: GET_PUBKEY_NORMAL,
&ctx, NULL, sl->d, &keyblock, 1);
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
log_error ("error reading key: %s\n", gpg_strerror (rc));
else if (opt.verbose)
log_info (_("key \"%s\" not found: %s\n"),
sl->d, gpg_strerror (rc));
}
else
{
do
{
listerr = list_keyblock (ctrl, keyblock, 0, 0,
opt.fingerprint, &listctx);
release_kbnode (keyblock);
}
while (!listerr && ctx && !getkey_next (ctrl, ctx, NULL, &keyblock));
getkey_end (ctrl, ctx);
ctx = NULL;
}
}
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&listctx);
keylist_context_release (&listctx);
}
static void
print_key_data (PKT_public_key * pk)
{
int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0;
int i;
for (i = 0; i < n; i++)
{
es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i]));
mpi_print (es_stdout, pk->pkey[i], 1);
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
}
/* Various public key screenings. (Right now just ROCA). With
* COLON_MODE set the output is formatted for use in the compliance
* field of a colon listing.
*/
static void
print_pk_screening (PKT_public_key *pk, int colon_mode)
{
gpg_error_t err;
if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo))
{
err = screen_key_for_roca (pk->pkey[0]);
if (!err)
;
else if (gpg_err_code (err) == GPG_ERR_TRUE)
{
if (colon_mode)
es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001);
else
es_fprintf (es_stdout,
" Screening: ROCA vulnerability detected\n");
}
else if (!colon_mode)
es_fprintf (es_stdout, " Screening: [ROCA check failed: %s]\n",
gpg_strerror (err));
}
}
static void
print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock)
{
unsigned int use = pk->pubkey_usage;
int c_printed = 0;
if (use & PUBKEY_USAGE_ENC)
es_putc ('e', es_stdout);
if (use & PUBKEY_USAGE_SIG)
{
es_putc ('s', es_stdout);
if (pk->flags.primary)
{
es_putc ('c', es_stdout);
/* The PUBKEY_USAGE_CERT flag was introduced later and we
used to always print 'c' for a primary key. To avoid any
regression here we better track whether we printed 'c'
already. */
c_printed = 1;
}
}
if ((use & PUBKEY_USAGE_CERT) && !c_printed)
es_putc ('c', es_stdout);
if ((use & PUBKEY_USAGE_AUTH))
es_putc ('a', es_stdout);
if (use & PUBKEY_USAGE_RENC)
es_putc ('r', es_stdout);
if ((use & PUBKEY_USAGE_TIME))
es_putc ('t', es_stdout);
if ((use & PUBKEY_USAGE_GROUP))
es_putc ('g', es_stdout);
if ((use & PUBKEY_USAGE_UNKNOWN))
es_putc ('?', es_stdout);
if (keyblock)
{
/* Figure out the usable capabilities. */
KBNODE k;
int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
pk = k->pkt->pkt.public_key;
if (pk->flags.primary)
disabled = pk_is_disabled (pk);
if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired)
{
if (pk->pubkey_usage & PUBKEY_USAGE_ENC)
enc = 1;
if (pk->pubkey_usage & PUBKEY_USAGE_SIG)
{
sign = 1;
if (pk->flags.primary)
cert = 1;
}
if (pk->pubkey_usage & PUBKEY_USAGE_CERT)
cert = 1;
if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH))
auth = 1;
}
}
}
if (enc)
es_putc ('E', es_stdout);
if (sign)
es_putc ('S', es_stdout);
if (cert)
es_putc ('C', es_stdout);
if (auth)
es_putc ('A', es_stdout);
if (disabled)
es_putc ('D', es_stdout);
}
es_putc (':', es_stdout);
}
/* FLAGS: 0x01 hashed
0x02 critical */
static void
print_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
const byte * buf)
{
size_t i;
es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len);
for (i = 0; i < len; i++)
{
/* printable ascii other than : and % */
if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%')
es_fprintf (es_stdout, "%c", buf[i]);
else
es_fprintf (es_stdout, "%%%02X", buf[i]);
}
es_fprintf (es_stdout, "\n");
}
void
print_subpackets_colon (PKT_signature * sig)
{
byte *i;
log_assert (opt.show_subpackets);
for (i = opt.show_subpackets; *i; i++)
{
const byte *p;
size_t len;
int seq, crit;
seq = 0;
while ((p = enum_sig_subpkt (sig, 1, *i, &len, &seq, &crit)))
print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p);
seq = 0;
while ((p = enum_sig_subpkt (sig, 0, *i, &len, &seq, &crit)))
print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p);
}
}
void
dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
{
int i;
if (!attrib_fp)
return;
for (i = 0; i < uid->numattribs; i++)
{
if (is_status_enabled ())
{
byte array[MAX_FINGERPRINT_LEN], *p;
char buf[(MAX_FINGERPRINT_LEN * 2) + 90];
size_t j, n;
if (!pk)
BUG ();
fingerprint_from_pk (pk, array, &n);
p = array;
for (j = 0; j < n; j++, p++)
sprintf (buf + 2 * j, "%02X", *p);
sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u",
(ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1,
uid->numattribs, (ulong) uid->created,
(ulong) uid->expiredate,
((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) |
(uid->flags.expired ? 0x04 : 0)));
write_status_text (STATUS_ATTRIBUTE, buf);
}
es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp);
es_fflush (attrib_fp);
}
}
static void
print_keygrip (const char *keygrip)
{
const char *s;
s = strchr (keygrip, ',');
if (s)
es_fprintf (es_stdout, " Keygrip = %.*s,\n%*s%s\n",
(int)(s-keygrip), keygrip, 16, "", s+1);
else
es_fprintf (es_stdout, " Keygrip = %s\n", keygrip);
}
/* If PK is given the output is written to a new file instead of
* stdout. */
static void
print_x509_notations (struct notation *nots, PKT_public_key *pk)
{
gpg_error_t err;
gpgrt_b64state_t state = NULL;
char hexfpr[2*4 + 1 + 2*MAX_FINGERPRINT_LEN+4+1];
char sha1[20];
estream_t fp;
for (; nots; nots = nots->next)
{
if (pk)
{
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1, nots->bdat, nots->blen);
bin2hex (sha1+16, 4, hexfpr);
hexfpr[2*4] = '-';
hexfingerprint (pk, hexfpr + 2*4+1, 2*MAX_FINGERPRINT_LEN);
strcat (hexfpr, ".pem");
fp = es_fopen (hexfpr, "w");
if (!fp)
{
err = gpg_err_code_from_syserror ();
goto b64fail;
}
}
else
fp = es_stdout;
state = gpgrt_b64enc_start (fp, "CERTIFICATE");
if (!state)
{
err = gpg_err_code_from_syserror ();
goto b64fail;
}
err = gpgrt_b64enc_write (state, nots->bdat, nots->blen);
if (err)
goto b64fail;
err = gpgrt_b64enc_finish (state);
if (err)
goto b64fail;
if (fp != es_stdout)
{
es_fclose (fp);
fp = NULL;
}
}
return;
b64fail:
log_error ("error writing base64 encoded notation: %s\n", gpg_strerror (err));
gpgrt_b64enc_finish (state);
if (fp && fp != es_stdout)
gpgrt_fcancel (fp);
}
/* Order two signatures. We first order by keyid and then by creation
* time. */
int
cmp_signodes (const void *av, const void *bv)
{
const kbnode_t an = *(const kbnode_t *)av;
const kbnode_t bn = *(const kbnode_t *)bv;
const PKT_signature *a;
const PKT_signature *b;
int i;
/* log_assert (an->pkt->pkttype == PKT_SIGNATURE); */
/* log_assert (bn->pkt->pkttype == PKT_SIGNATURE); */
a = an->pkt->pkt.signature;
b = bn->pkt->pkt.signature;
/* Self-signatures are ordered first. */
if ((an->flag & NODFLG_MARK_B) && !(bn->flag & NODFLG_MARK_B))
return -1;
if (!(an->flag & NODFLG_MARK_B) && (bn->flag & NODFLG_MARK_B))
return 1;
/* then the keyids. (which are or course the same for self-sigs). */
i = keyid_cmp (a->keyid, b->keyid);
if (i)
return i;
/* Followed by creation time */
if (a->timestamp > b->timestamp)
return 1;
if (a->timestamp < b->timestamp)
return -1;
/* followed by the class in a way that a rev comes first. */
if (a->sig_class > b->sig_class)
return 1;
if (a->sig_class < b->sig_class)
return -1;
/* To make the sort stable we compare the entire structure as last resort. */
return memcmp (a, b, sizeof *a);
}
/* Given a domain name at NAME with length NAME, check whether this is
* a valid domain name and in that case return a malloced string ith
* the name. Escaped dots are ignored and removed from the result.
* Example: "example\.org" -> "example.org" Note that the input may
* not be Nul terminated. */
static char *
parse_trust_name (const char *name, size_t namelen)
{
char *buffer, *p;
p = buffer = xtrymalloc (namelen+1);
if (!buffer)
return NULL; /* Oops - caller needs to use some fallback */
for (; namelen; name++, namelen--)
{
if (*name == '\\' && namelen > 1 && name[1] == '.')
; /* Skip the escape character. */
else
*p++ = *name;
}
*p = 0;
if (!is_valid_domain_name (buffer))
{
xfree (buffer);
buffer = NULL;
}
return buffer;
}
void
print_revocation_reason_comment (const char *comment, size_t comment_len)
{
const byte *s, *s_lf;
size_t n, n_lf;
if (!comment || !comment_len)
return;
s = comment;
n = comment_len;
s_lf = NULL;
do
{
/* We don't want any empty lines, so we skip them. */
for (;n && *s == '\n'; s++, n--)
;
if (n)
{
s_lf = memchr (s, '\n', n);
n_lf = s_lf? s_lf - s : n;
es_fprintf (es_stdout, " %s",
_("revocation comment: "));
es_write_sanitized (es_stdout, s, n_lf, NULL, NULL);
es_putc ('\n', es_stdout);
s += n_lf; n -= n_lf;
}
} while (s_lf);
}
static void
print_revocation_reason (PKT_public_key *pk)
{
char *freeme;
const char *codestr;
if (!pk->revoked.got_reason)
return;
if (!pk->revoked.reason_code && !pk->revoked.reason_comment)
return; /* Do not print "revocation reason: No reason specified". */
codestr = revocation_reason_code_to_str (pk->revoked.reason_code, &freeme);
es_fprintf (es_stdout, " %s%s\n",
_("reason for revocation: "), codestr);
xfree (freeme);
print_revocation_reason_comment (pk->revoked.reason_comment,
pk->revoked.reason_comment_len);
}
/* Helper for list_keyblock_print. The caller must have set
* NODFLG_MARK_B to indicate self-signatures. */
static void
list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node,
struct keylist_context *listctx, PKT_public_key *lastpk)
{
PKT_signature *sig = node->pkt->pkt.signature;
int rc, sigrc;
char *sigstr;
char *reason_text = NULL;
char *reason_comment = NULL;
size_t reason_commentlen;
int reason_code = 0;
if (listctx->check_sigs)
{
rc = check_key_signature (ctrl, keyblock, node, NULL);
switch (gpg_err_code (rc))
{
case 0:
listctx->good_sigs++;
sigrc = '!';
break;
case GPG_ERR_BAD_SIGNATURE:
listctx->inv_sigs++;
sigrc = '-';
break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY:
listctx->no_key++;
return;
case GPG_ERR_DIGEST_ALGO:
case GPG_ERR_PUBKEY_ALGO:
if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS))
return;
/* fallthru. */
default:
listctx->oth_err++;
sigrc = '%';
break;
}
/* TODO: Make sure a cached sig record here still has
the pk that issued it. See also
keyedit.c:print_and_check_one_sig */
}
else
{
if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)
&& (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo)
== GPG_ERR_PUBKEY_ALGO)
|| gpg_err_code (openpgp_md_test_algo (sig->digest_algo)
== GPG_ERR_DIGEST_ALGO)
|| (sig->digest_algo == DIGEST_ALGO_SHA1
&& !(node->flag & NODFLG_MARK_B) /*no selfsig*/
&& !opt.flags.allow_weak_key_signatures)))
return;
rc = 0;
sigrc = ' ';
}
if (IS_KEY_REV (sig) || IS_SUBKEY_REV (sig) || IS_UID_REV (sig))
{
sigstr = "rev";
reason_code = get_revocation_reason (sig, &reason_text,
&reason_comment,
&reason_commentlen);
}
else if (IS_UID_SIG (sig))
sigstr = "sig";
else if (IS_SUBKEY_SIG (sig))
sigstr = "sig";
else if (IS_KEY_SIG (sig))
sigstr = "sig";
else
{
es_fprintf (es_stdout, "sig "
"[unexpected signature class 0x%02x]\n",
sig->sig_class);
return;
}
es_fputs (sigstr, es_stdout);
es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s",
sigrc, (sig->sig_class - 0x10 > 0 &&
sig->sig_class - 0x10 <
4) ? '0' + sig->sig_class - 0x10 : ' ',
sig->flags.exportable ? ' ' : 'L',
sig->flags.revocable ? ' ' : 'R',
sig->flags.policy_url ? 'P' : ' ',
sig->flags.notation ? 'N' : ' ',
sig->flags.expired ? 'X' : ' ',
(sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
0) ? '0' +
sig->trust_depth : ' ', keystr (sig->keyid),
datestr_from_sig (sig));
if (opt.list_options & LIST_SHOW_SIG_EXPIRE)
es_fprintf (es_stdout, " %s", expirestr_from_sig (sig));
es_fprintf (es_stdout, " ");
if (sigrc == '%')
es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
else if (sigrc == '?')
;
else if ((node->flag & NODFLG_MARK_B))
es_fputs (_("[self-signature]"), es_stdout);
else if (!opt.fast_list_mode )
{
size_t n;
char *p = get_user_id (ctrl, sig->keyid, &n, NULL);
print_utf8_buffer (es_stdout, p, n);
xfree (p);
}
if ((opt.list_options & LIST_SHOW_TRUSTSIG)
&& (sig->trust_depth || sig->trust_value || sig->trust_regexp))
{
es_fprintf (es_stdout, " [T=%d,%d", sig->trust_depth, sig->trust_value);
if (sig->trust_regexp)
{
size_t n = strlen (sig->trust_regexp);
char *tname = NULL;
if (!strncmp (sig->trust_regexp, "<[^>]+[@.]", 10)
&& n > 12 && !strcmp (sig->trust_regexp+n-2, ">$")
&& (tname=parse_trust_name (sig->trust_regexp+10, n-12)))
{
es_fprintf (es_stdout, ",\"%s", tname);
xfree (tname);
}
else
{
es_fputs (",R\"", es_stdout);
es_write_sanitized (es_stdout, sig->trust_regexp, n, "\"", NULL);
}
es_putc ('\"', es_stdout);
}
es_putc (']', es_stdout);
}
es_putc ('\n', es_stdout);
if (sig->flags.policy_url
&& (opt.list_options & LIST_SHOW_POLICY_URLS))
show_policy_url (sig, 3, 0);
if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
show_notation (sig, 3, 0,
((opt.
list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
((opt.
list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0) +
((opt.
list_options & LIST_SHOW_HIDDEN_NOTATIONS) ? 4 : 0));
if (sig->flags.notation
&& (opt.list_options
& (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS)))
{
struct notation *nots;
if ((IS_KEY_SIG (sig) || IS_SUBKEY_SIG (sig))
&& (nots = search_sig_notations (sig,
"x509certificate@pgp.com")))
{
if ((opt.list_options & LIST_STORE_X509_NOTATIONS))
print_x509_notations (nots, lastpk);
else
print_x509_notations (nots, NULL);
free_notation (nots);
}
}
if (sig->flags.pref_ks
&& (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 3, 0);
if (reason_text && (reason_code || reason_comment))
{
es_fprintf (es_stdout, " %s%s\n",
_("reason for revocation: "), reason_text);
print_revocation_reason_comment (reason_comment, reason_commentlen);
}
xfree (reason_text);
xfree (reason_comment);
/* fixme: check or list other sigs here */
}
static void
list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
struct keylist_context *listctx)
{
int rc;
kbnode_t node;
PKT_public_key *pk;
PKT_public_key *lastpk;
u32 *mainkid;
int skip_sigs = 0;
char *hexgrip = NULL;
char *serialno = NULL;
/* Get the keyid from the keyblock. */
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
pk = node->pkt->pkt.public_key;
mainkid = pk_keyid (pk);
lastpk = pk;
if (secret || opt.with_keygrip)
{
rc = hexkeygrip_from_pk (pk, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
}
if (secret)
{
/* Encode some info about the secret key in SECRET. */
if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
secret = serialno? 3 : 1;
else
secret = 2; /* Key not found. */
}
if (!listctx->no_validity)
check_trustdb_stale (ctrl);
/* Print the "pub" line and in KF_NONE mode the fingerprint. */
print_key_line (ctrl, es_stdout, pk, secret);
if (fpr)
print_fingerprint (ctrl, NULL, pk, 0);
if (opt.with_keygrip && hexgrip)
print_keygrip (hexgrip);
if (serialno)
print_card_serialno (serialno);
if (opt.with_key_data)
print_key_data (pk);
if (opt.with_key_screening)
print_pk_screening (pk, 0);
if (opt.with_key_origin
&& (pk->keyorg || pk->keyupdate || pk->updateurl))
{
char updatestr[MK_DATESTR_SIZE];
es_fprintf (es_stdout, " origin=%s last=%s %s",
key_origin_string (pk->keyorg),
mk_datestr (updatestr, sizeof updatestr, pk->keyupdate),
pk->updateurl? "url=":"");
if (pk->updateurl)
print_utf8_string (es_stdout, pk->updateurl);
es_putc ('\n', es_stdout);
}
print_revokers (es_stdout, 0, pk);
for (node = keyblock; node; node = node->next)
{
if (is_deleted_kbnode (node))
continue;
if (node->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = node->pkt->pkt.user_id;
int indent;
int kl = opt.keyid_format == KF_NONE? 10 : keystrlen ();
if ((uid->flags.expired || uid->flags.revoked)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
{
skip_sigs = 1;
continue;
}
else
skip_sigs = 0;
if (attrib_fp && uid->attrib_data != NULL)
dump_attribs (uid, pk);
if ((uid->flags.revoked || uid->flags.expired)
|| ((opt.list_options & LIST_SHOW_UID_VALIDITY)
&& !listctx->no_validity))
{
const char *validity;
validity = uid_trust_string_fixed (ctrl, pk, uid);
indent = ((kl + (opt.legacy_list_mode? 9:11))
- atoi (uid_trust_string_fixed (ctrl, NULL, NULL)));
if (indent < 0 || indent > 40)
indent = 0;
es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
}
else
{
indent = kl + (opt.legacy_list_mode? 10:12);
es_fprintf (es_stdout, "uid%*s", indent, "");
}
print_utf8_buffer (es_stdout, uid->name, uid->len);
es_putc ('\n', es_stdout);
if ((opt.list_options & LIST_SHOW_PREF_VERBOSE))
show_preferences (uid, indent+2, 0, 1);
else if ((opt.list_options & LIST_SHOW_PREF))
show_preferences (uid, indent+2, 0, 0);
if (opt.with_wkd_hash)
{
char *mbox, *hash, *p;
char hashbuf[32];
mbox = mailbox_from_userid (uid->name, 0);
if (mbox && (p = strchr (mbox, '@')))
{
*p++ = 0;
gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
mbox, strlen (mbox));
hash = zb32_encode (hashbuf, 8*20);
if (hash)
{
es_fprintf (es_stdout, " %*s%s@%s\n",
indent, "", hash, p);
xfree (hash);
}
}
xfree (mbox);
}
if (opt.with_key_origin
&& (uid->keyorg || uid->keyupdate || uid->updateurl))
{
char updatestr[MK_DATESTR_SIZE];
es_fprintf (es_stdout, " %*sorigin=%s last=%s %s",
indent, "",
key_origin_string (uid->keyorg),
mk_datestr (updatestr, sizeof updatestr,
uid->keyupdate),
uid->updateurl? "url=":"");
if (uid->updateurl)
print_utf8_string (es_stdout, uid->updateurl);
es_putc ('\n', es_stdout);
}
if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
PKT_public_key *pk2 = node->pkt->pkt.public_key;
lastpk = pk2;
if ((pk2->flags.revoked || pk2->has_expired)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS))
{
skip_sigs = 1;
continue;
}
else
skip_sigs = 0;
xfree (serialno); serialno = NULL;
xfree (hexgrip); hexgrip = NULL;
if (secret || opt.with_keygrip)
{
rc = hexkeygrip_from_pk (pk2, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n",
gpg_strerror (rc));
}
if (secret)
{
if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
secret = serialno? 3 : 1;
else
secret = 2; /* Key not found. */
}
/* Print the "sub" line. */
print_key_line (ctrl, es_stdout, pk2, secret);
if (fpr > 1 || opt.with_subkey_fingerprint)
{
print_fingerprint (ctrl, NULL, pk2, 0);
if (serialno)
print_card_serialno (serialno);
}
if (opt.with_keygrip && hexgrip)
print_keygrip (hexgrip);
if (opt.with_key_data)
print_key_data (pk2);
if (opt.with_key_screening)
print_pk_screening (pk2, 0);
}
else if ((opt.list_sigs
|| (opt.list_options
& (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS)))
&& node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs)
{
kbnode_t n;
unsigned int sigcount = 0;
kbnode_t *sigarray;
unsigned int idx;
for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
sigcount++;
sigarray = xcalloc (sigcount, sizeof *sigarray);
sigcount = 0;
for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
{
if (keyid_eq (mainkid, n->pkt->pkt.signature->keyid))
n->flag |= NODFLG_MARK_B; /* Is a self-sig. */
else
n->flag &= ~NODFLG_MARK_B;
sigarray[sigcount++] = node = n;
}
/* Note that NODE is now at the last signature. */
if ((opt.list_options & LIST_SORT_SIGS))
qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes);
for (idx=0; idx < sigcount; idx++)
list_signature_print (ctrl, keyblock, sigarray[idx], listctx,
lastpk);
xfree (sigarray);
}
}
es_putc ('\n', es_stdout);
xfree (serialno);
xfree (hexgrip);
}
/* Do a simple key listing printing only the fingerprint and the mail
* address of valid keys. */
static void
list_keyblock_simple (ctrl_t ctrl, kbnode_t keyblock)
{
gpg_err_code_t ec;
kbnode_t kbctx;
kbnode_t node;
char hexfpr[2*MAX_FINGERPRINT_LEN+1];
char *mbox;
(void)ctrl;
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr);
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
{
if (node->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = node->pkt->pkt.user_id;
if (uid->attrib_data)
continue;
if ((uid->flags.expired || uid->flags.revoked)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
continue;
mbox = mailbox_from_userid (uid->name, 0);
if (!mbox)
{
ec = gpg_err_code_from_syserror ();
if (ec != GPG_ERR_EINVAL)
log_error ("error getting mailbox from user-id: %s\n",
gpg_strerror (ec));
continue;
}
es_fprintf (es_stdout, "%s %s\n", hexfpr, mbox);
xfree (mbox);
}
}
}
/* Print the revoker records. */
void
print_revokers (estream_t fp, int colon_mode, PKT_public_key * pk)
{
int i, j;
const byte *p;
if (!pk->revkey && pk->numrevkeys)
BUG ();
for (i = 0; i < pk->numrevkeys; i++)
{
if (colon_mode)
{
es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid);
p = pk->revkey[i].fpr;
for (j = 0; j < pk->revkey[i].fprlen; j++, p++)
es_fprintf (fp, "%02X", *p);
es_fprintf (fp, ":%02x%s:\n",
pk->revkey[i].class,
(pk->revkey[i].class & 0x40) ? "s" : "");
}
else
{
es_fprintf (fp, "%*s%s", 6, "", _("Revocable by: "));
p = pk->revkey[i].fpr;
es_write_hexstring (fp, pk->revkey[i].fpr, pk->revkey[i].fprlen,
0, NULL);
if ((pk->revkey[i].class & 0x40))
es_fprintf (fp, " %s", _("(sensitive)"));
/* Class bit 7 must always be set, bit 6 indicates sensitive
* and all others bits are reserved. */
if (!(pk->revkey[i].class & ~0x40)
|| (pk->revkey[i].class & ~(0x40|0x80)))
es_fprintf (fp, " (unknown class %02x)", pk->revkey[i].class);
es_fprintf (fp, "\n");
}
}
}
/* Print the compliance flags to field 18. PK is the public key.
* KEYLENGTH is the length of the key in bits and CURVENAME is either
* NULL or the name of the curve. The latter two args are here
* merely because the caller has already computed them. */
static void
print_compliance_flags (PKT_public_key *pk,
unsigned int keylength, const char *curvename)
{
int any = 0;
if (!keylength)
keylength = nbits_from_pk (pk);
if (pk->version == 5)
{
es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout);
any++;
}
if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
keylength, curvename))
{
es_fprintf (es_stdout, any ? " %s" : "%s",
gnupg_status_compliance_flag (CO_DE_VS));
any++;
}
if (opt.with_key_screening)
print_pk_screening (pk, 1+any);
}
/* List a key in colon mode. If SECRET is true this is a secret key
record (i.e. requested via --list-secret-key). If HAS_SECRET a
secret key is available even if SECRET is not set. */
static void
list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
int secret, int has_secret)
{
int rc;
KBNODE kbctx;
KBNODE node;
PKT_public_key *pk;
u32 keyid[2];
int trustletter = 0;
int trustletter_print;
int ownertrust_print;
int ulti_hack = 0;
int i;
char *hexgrip_buffer = NULL;
const char *hexgrip = NULL;
char *serialno = NULL;
int stubkey;
unsigned int keylength;
char *curve = NULL;
const char *curvename = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE];
/* Get the keyid from the keyblock. */
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
pk = node->pkt->pkt.public_key;
if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
{
rc = hexkeygrip_from_pk (pk, &hexgrip_buffer);
if (rc)
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
/* In the error case we print an empty string so that we have a
* "grp" record for each primary and subkey - even if it is
* empty. This may help to prevent sync problems. */
hexgrip = hexgrip_buffer? hexgrip_buffer : "";
}
stubkey = 0;
if ((secret || has_secret)
&& agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
stubkey = 1; /* Key not found. */
keyid_from_pk (pk, keyid);
if (!pk->flags.valid)
trustletter_print = 'i';
else if (pk->flags.revoked)
trustletter_print = 'r';
else if (pk->has_expired)
trustletter_print = 'e';
else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
trustletter_print = 0;
else
{
trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
if (trustletter == 'u')
ulti_hack = 1;
trustletter_print = trustletter;
}
if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
ownertrust_print = get_ownertrust_info (ctrl, pk, 0);
else
ownertrust_print = 0;
keylength = nbits_from_pk (pk);
es_fputs (secret? "sec:":"pub:", es_stdout);
if (trustletter_print)
es_putc (trustletter_print, es_stdout);
es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
keylength,
pk->pubkey_algo,
(ulong) keyid[0], (ulong) keyid[1],
colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
if (ownertrust_print)
es_putc (ownertrust_print, es_stdout);
es_putc (':', es_stdout);
es_putc (':', es_stdout);
es_putc (':', es_stdout);
print_capabilities (ctrl, pk, keyblock);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret || has_secret)
{
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs (serialno, es_stdout);
else if (has_secret)
es_putc ('+', es_stdout);
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
curve = openpgp_oid_to_str (pk->pkey[0]);
curvename = openpgp_oid_to_curve (curve, 0);
if (!curvename)
curvename = curve;
es_fputs (curvename, es_stdout);
}
else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
/* Note that Kyber should actually not appear here because it is
* the primary key and Kyber is not able to certify. But we
* prepare it here for future composite algorithms and in case
* of faulty packets. */
es_fputs (pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), es_stdout);
}
es_putc (':', es_stdout); /* End of field 17. */
print_compliance_flags (pk, keylength, curvename);
es_putc (':', es_stdout); /* End of field 18 (compliance). */
if (pk->keyupdate)
es_fputs (colon_strtime (pk->keyupdate), es_stdout);
es_putc (':', es_stdout); /* End of field 19 (last_update). */
es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":"");
if (pk->updateurl)
es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl),
":", NULL);
es_putc (':', es_stdout); /* End of field 20 (origin). */
if (pk->flags.revoked && pk->revoked.got_reason
&& (pk->revoked.reason_code || pk->revoked.reason_comment))
{
char *freeme;
const char *s;
size_t n;
s = revocation_reason_code_to_str (pk->revoked.reason_code, &freeme);
n = strlen (s);
es_write_sanitized (es_stdout, s, n, ":", NULL);
if (n && s[n-1] != '.')
es_putc ('.', es_stdout);
es_putc ('\\', es_stdout); /* C-style escaped colon. */
es_putc ('n', es_stdout);
es_write_sanitized (es_stdout, pk->revoked.reason_comment,
pk->revoked.reason_comment_len,
":", NULL);
xfree (freeme);
es_putc (':', es_stdout); /* End of field 21 (comment). */
}
es_putc ('\n', es_stdout);
print_revokers (es_stdout, 1, pk);
print_fingerprint (ctrl, NULL, pk, 0);
if (hexgrip)
es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
if (opt.with_key_data)
print_key_data (pk);
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
{
if (node->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = node->pkt->pkt.user_id;
int uid_validity;
if (attrib_fp && uid->attrib_data != NULL)
dump_attribs (uid, pk);
if (uid->flags.revoked)
uid_validity = 'r';
else if (uid->flags.expired)
uid_validity = 'e';
else if (opt.no_expensive_trust_checks)
uid_validity = 0;
else if (ulti_hack)
uid_validity = 'u';
else
uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
if (uid_validity)
es_putc (uid_validity, es_stdout);
es_fputs ("::::", es_stdout);
es_fprintf (es_stdout, "%s:", colon_strtime (uid->created));
es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate));
namehash_from_uid (uid);
for (i = 0; i < 20; i++)
es_fprintf (es_stdout, "%02X", uid->namehash[i]);
es_fprintf (es_stdout, "::");
if (uid->attrib_data)
es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
else
es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
es_fputs (":::::::::", es_stdout);
if (uid->keyupdate)
es_fputs (colon_strtime (uid->keyupdate), es_stdout);
es_putc (':', es_stdout); /* End of field 19 (last_update). */
es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":"");
if (uid->updateurl)
es_write_sanitized (es_stdout,
uid->updateurl, strlen (uid->updateurl),
":", NULL);
es_putc (':', es_stdout); /* End of field 20 (origin). */
es_putc ('\n', es_stdout);
#ifdef USE_TOFU
if (!uid->attrib_data && opt.with_tofu_info
&& (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP))
{
/* Print a "tfs" record. */
tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name);
}
#endif /*USE_TOFU*/
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
u32 keyid2[2];
PKT_public_key *pk2;
int need_hexgrip = !!hexgrip;
pk2 = node->pkt->pkt.public_key;
xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL;
xfree (serialno); serialno = NULL;
if (need_hexgrip
|| secret || has_secret || opt.with_keygrip || opt.with_key_data)
{
rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer);
if (rc)
log_error ("error computing a keygrip: %s\n",
gpg_strerror (rc));
hexgrip = hexgrip_buffer? hexgrip_buffer : "";
}
stubkey = 0;
if ((secret||has_secret)
&& agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
stubkey = 1; /* Key not found. */
keyid_from_pk (pk2, keyid2);
es_fputs (secret? "ssb:":"sub:", es_stdout);
if (!pk2->flags.valid)
es_putc ('i', es_stdout);
else if (pk2->flags.revoked)
es_putc ('r', es_stdout);
else if (pk2->has_expired)
es_putc ('e', es_stdout);
else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
;
else
{
/* TRUSTLETTER should always be defined here. */
if (trustletter)
es_fprintf (es_stdout, "%c", trustletter);
}
keylength = nbits_from_pk (pk2);
es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
keylength,
pk2->pubkey_algo,
(ulong) keyid2[0], (ulong) keyid2[1],
colon_datestr_from_pk (pk2),
colon_strtime (pk2->expiredate));
print_capabilities (ctrl, pk2, NULL);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret || has_secret)
{
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs (serialno, es_stdout);
else if (has_secret)
es_putc ('+', es_stdout);
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
{
xfree (curve);
curve = openpgp_oid_to_str (pk2->pkey[0]);
curvename = openpgp_oid_to_curve (curve, 0);
if (!curvename)
curvename = curve;
es_fputs (curvename, es_stdout);
}
else if (pk2->pubkey_algo == PUBKEY_ALGO_KYBER)
{
es_fputs (pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
es_stdout);
}
es_putc (':', es_stdout); /* End of field 17. */
print_compliance_flags (pk2, keylength, curvename);
es_putc (':', es_stdout); /* End of field 18. */
es_putc ('\n', es_stdout);
print_fingerprint (ctrl, NULL, pk2, 0);
if (hexgrip)
es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
if (opt.with_key_data)
print_key_data (pk2);
}
else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc, fprokay = 0;
char *sigstr;
size_t fplen;
byte fparray[MAX_FINGERPRINT_LEN];
char *siguid;
size_t siguidlen;
char *issuer_fpr = NULL;
char *reason_text = NULL;
char *reason_comment = NULL;
size_t reason_commentlen;
int reason_code = 0; /* Init to silence compiler warning. */
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
{
sigstr = "rev";
reason_code = get_revocation_reason (sig, &reason_text,
&reason_comment,
&reason_commentlen);
}
else if ((sig->sig_class & ~3) == 0x10)
sigstr = "sig";
else if (sig->sig_class == 0x18)
sigstr = "sig";
else if (sig->sig_class == 0x1F)
sigstr = "sig";
else
{
es_fprintf (es_stdout, "sig::::::::::%02x%c:\n",
sig->sig_class, sig->flags.exportable ? 'x' : 'l');
continue;
}
if (opt.check_sigs)
{
PKT_public_key *signer_pk = NULL;
es_fflush (es_stdout);
if (opt.no_sig_cache)
signer_pk = xmalloc_clear (sizeof (PKT_public_key));
rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk,
NULL, NULL, NULL);
switch (gpg_err_code (rc))
{
case 0:
sigrc = '!';
break;
case GPG_ERR_BAD_SIGNATURE:
sigrc = '-';
break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY:
sigrc = '?';
break;
default:
sigrc = '%';
break;
}
if (opt.no_sig_cache)
{
if (!rc)
{
fingerprint_from_pk (signer_pk, fparray, &fplen);
fprokay = 1;
}
free_public_key (signer_pk);
}
}
else
{
rc = 0;
sigrc = ' '; /* Note the fix-up below in --list-sigs mode. */
}
if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
{
int nouid;
siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid);
if (!opt.check_sigs && nouid)
sigrc = '?'; /* No key in local keyring. */
}
else
{
siguid = NULL;
siguidlen = 0;
}
es_fputs (sigstr, es_stdout);
es_putc (':', es_stdout);
if (sigrc != ' ')
es_putc (sigrc, es_stdout);
es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
(ulong) sig->keyid[0], (ulong) sig->keyid[1],
colon_datestr_from_sig (sig),
colon_expirestr_from_sig (sig));
if (sig->trust_depth || sig->trust_value)
es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value);
es_fprintf (es_stdout, ":");
if (sig->trust_regexp)
es_write_sanitized (es_stdout, sig->trust_regexp,
strlen (sig->trust_regexp), ":", NULL);
es_fprintf (es_stdout, ":");
if (sigrc == '%')
es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
else if (siguid)
es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
es_fprintf (es_stdout, ":%02x%c", sig->sig_class,
sig->flags.exportable ? 'x' : 'l');
if (reason_text)
es_fprintf (es_stdout, ",%02x", reason_code);
es_fputs ("::", es_stdout);
if (opt.no_sig_cache && opt.check_sigs && fprokay)
{
for (i = 0; i < fplen; i++)
es_fprintf (es_stdout, "%02X", fparray[i]);
}
else if ((issuer_fpr = issuer_fpr_string (sig)))
es_fputs (issuer_fpr, es_stdout);
es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
if (reason_comment)
{
es_fputs ("::::", es_stdout);
es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
":", NULL);
es_putc (':', es_stdout);
}
es_putc ('\n', es_stdout);
if (opt.show_subpackets)
print_subpackets_colon (sig);
/* fixme: check or list other sigs here */
xfree (reason_text);
xfree (reason_comment);
xfree (siguid);
xfree (issuer_fpr);
}
}
xfree (curve);
xfree (hexgrip_buffer);
xfree (serialno);
}
/*
* Reorder the keyblock so that the primary user ID (and not attribute
* packet) comes first. Fixme: Replace this by a generic sort
* function. */
static void
do_reorder_keyblock (KBNODE keyblock, int attr)
{
KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
KBNODE last, node;
for (node = keyblock; node; primary0 = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID &&
((attr && node->pkt->pkt.user_id->attrib_data) ||
(!attr && !node->pkt->pkt.user_id->attrib_data)) &&
node->pkt->pkt.user_id->flags.primary)
{
primary = primary2 = node;
for (node = node->next; node; primary2 = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
break;
}
}
break;
}
}
if (!primary)
return; /* No primary key flag found (should not happen). */
for (last = NULL, node = keyblock; node; last = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID)
break;
}
log_assert (node);
log_assert (last); /* The user ID is never the first packet. */
log_assert (primary0); /* Ditto (this is the node before primary). */
if (node == primary)
return; /* Already the first one. */
last->next = primary;
primary0->next = primary2->next;
primary2->next = node;
}
void
reorder_keyblock (KBNODE keyblock)
{
do_reorder_keyblock (keyblock, 1);
do_reorder_keyblock (keyblock, 0);
}
/* Note: If this function returns an error the caller is expected to
* honor this and stop all further processing. Any error returned
* will be a write error (to stdout) and a diagnostics is always
* printed using log_error. */
static gpg_error_t
list_keyblock (ctrl_t ctrl,
KBNODE keyblock, int secret, int has_secret, int fpr,
struct keylist_context *listctx)
{
gpg_error_t err = 0;
es_clearerr (es_stdout);
reorder_keyblock (keyblock);
if (list_filter.selkey)
{
int selected = 0;
struct impex_filter_parm_s parm;
parm.ctrl = ctrl;
for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
{
if (recsel_select (list_filter.selkey, impex_filter_getval, &parm))
{
selected = 1;
break;
}
}
if (!selected)
return 0; /* Skip this one. */
}
if (opt.with_colons)
list_keyblock_colon (ctrl, keyblock, secret, has_secret);
else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
{
if (!listctx->no_validity)
check_trustdb_stale (ctrl);
list_keyblock_simple (ctrl, keyblock);
}
else
list_keyblock_print (ctrl, keyblock, secret, fpr, listctx);
if (es_ferror (es_stdout))
err = gpg_error_from_syserror ();
if (secret && es_fflush (es_stdout) && !err)
err = gpg_error_from_syserror ();
if (err)
log_error (_("error writing to stdout: %s\n"), gpg_strerror (err));
return err;
}
/* Public function used by keygen to list a keyblock. If NO_VALIDITY
* is set the validity of a key is never shown. */
gpg_error_t
list_keyblock_direct (ctrl_t ctrl,
kbnode_t keyblock, int secret, int has_secret, int fpr,
int no_validity)
{
struct keylist_context listctx;
gpg_error_t err;
memset (&listctx, 0, sizeof (listctx));
listctx.no_validity = !!no_validity;
if (opt.check_sigs)
listctx.check_sigs = 1;
err = list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx);
keylist_context_release (&listctx);
return err;
}
/* Print an hex digit in ICAO spelling. */
static void
print_icao_hexdigit (estream_t fp, int c)
{
static const char *list[16] = {
"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
"Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"
};
tty_fprintf (fp, "%s", list[c&15]);
}
/*
* Function to print the finperprint.
* mode 0: as used in key listings, opt.with_colons is honored
* 1: print using log_info ()
* 2: direct use of tty
* 3: direct use of tty but only primary key.
* 4: direct use of tty but only subkey.
* 10: Same as 0 but with_colons etc is ignored.
* 20: Same as 0 but using a compact format.
*
* Modes 1 and 2 will try and print both subkey and primary key
* fingerprints. A MODE with bit 7 set is used internally. If
* OVERRIDE_FP is not NULL that stream will be used in 0 instead
* of es_stdout or instead of the TTY in modes 2 and 3.
*/
void
print_fingerprint (ctrl_t ctrl, estream_t override_fp,
PKT_public_key *pk, int mode)
{
char hexfpr[2*MAX_FINGERPRINT_LEN+1];
char *p;
size_t i;
estream_t fp;
const char *text;
int primary = 0;
int with_colons = opt.with_colons;
int with_icao = opt.with_icao_spelling;
int compact = 0;
if (mode == 10)
{
mode = 0;
with_colons = 0;
with_icao = 0;
}
else if (mode == 20)
{
mode = 0;
with_colons = 0;
compact = 1;
}
if (!opt.fingerprint && !opt.with_fingerprint
&& opt.with_subkey_fingerprint)
compact = 1;
if (pk->main_keyid[0] == pk->keyid[0]
&& pk->main_keyid[1] == pk->keyid[1])
primary = 1;
/* Just to be safe */
if ((mode & 0x80) && !primary)
{
log_error ("primary key is not really primary!\n");
return;
}
mode &= ~0x80;
if (!primary && (mode == 1 || mode == 2))
{
PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk));
get_pubkey (ctrl, primary_pk, pk->main_keyid);
print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80));
free_public_key (primary_pk);
}
if (mode == 1)
{
fp = log_get_stream ();
if (primary)
text = _("Primary key fingerprint:");
else
text = _(" Subkey fingerprint:");
}
else if (mode == 2)
{
fp = override_fp; /* Use tty or given stream. */
if (primary)
/* TRANSLATORS: this should fit into 24 bytes so that the
* fingerprint data is properly aligned with the user ID */
text = _(" Primary key fingerprint:");
else
text = _(" Subkey fingerprint:");
}
else if (mode == 3)
{
fp = override_fp; /* Use tty or given stream. */
text = _(" Key fingerprint =");
}
else if (mode == 4)
{
fp = override_fp; /* Use tty or given stream. */
text = _(" Subkey fingerprint:");
}
else
{
fp = override_fp? override_fp : es_stdout;
if (opt.keyid_format == KF_NONE)
{
text = " "; /* To indent ICAO spelling. */
compact = 1;
}
else
text = _(" Key fingerprint =");
}
hexfingerprint (pk, hexfpr, sizeof hexfpr);
if (with_colons && !mode)
{
es_fprintf (fp, "fpr:::::::::%s:", hexfpr);
if (opt.with_v5_fingerprint && pk->version == 4)
{
char *v5fpr = v5hexfingerprint (pk, NULL, 0);
es_fprintf (fp, "\nfp2:::::::::%s:", v5fpr);
xfree (v5fpr);
}
}
else if (compact && !opt.fingerprint && !opt.with_fingerprint)
{
tty_fprintf (fp, "%*s%s", 6, "", hexfpr);
}
else
{
char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1];
format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr);
if (compact)
tty_fprintf (fp, "%*s%s", 6, "", fmtfpr);
else
tty_fprintf (fp, "%s %s", text, fmtfpr);
}
tty_fprintf (fp, "\n");
if (!with_colons && with_icao)
{
;
tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, "");
for (i = 0, p = hexfpr; *p; i++, p++)
{
if (!i)
;
else if (!(i%10))
tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, "");
else if (!(i%5))
tty_fprintf (fp, " ");
else
tty_fprintf (fp, " ");
print_icao_hexdigit (fp, xtoi_1 (p));
}
tty_fprintf (fp, "\"\n");
}
}
/* Print the serial number of an OpenPGP card if available. */
static void
print_card_serialno (const char *serialno)
{
if (!serialno)
return;
if (opt.with_colons)
return; /* Handled elsewhere. */
es_fputs (_(" Card serial no. ="), es_stdout);
es_putc (' ', es_stdout);
if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12))
{
/* This is an OpenPGP card. Print the relevant part. */
/* Example: D2760001240101010001000003470000 */
/* xxxxyyyyyyyy */
es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20);
}
else
es_fputs (serialno, es_stdout);
es_putc ('\n', es_stdout);
}
/* Print a public or secret (sub)key line. Example:
*
* pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31]
* 80615870F5BAD690333686D0F2AD85AC1E42B367
*
* pub rsa2048 2017-12-31 [SC] [expires: 2028-12-31]
* 80615870F5BAD690333686D0F2AD85AC1E42B3671122334455
*
* Some global options may result in a different output format. If
* SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and
* depending on the value a flag character is shown:
*
* 1 := ' ' Regular secret key
* 2 := '#' Stub secret key
* 3 := '>' Secret key is on a token.
*/
void
print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret)
{
char pkstrbuf[PUBKEY_STRING_SIZE];
tty_fprintf (fp, "%s%c %s",
pk->flags.primary? (secret? "sec":"pub")
/**/ : (secret? "ssb":"sub"),
secret == 2? '#' : secret == 3? '>' : ' ',
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf));
if (opt.keyid_format != KF_NONE)
tty_fprintf (fp, "/%s", keystr_from_pk (pk));
tty_fprintf (fp, " %s", datestr_from_pk (pk));
if (pk->flags.primary
&& !(openpgp_pk_algo_usage (pk->pubkey_algo)
& (PUBKEY_USAGE_CERT| PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)))
{
/* A primary key which is really not capable to sign. */
tty_fprintf (fp, " [INVALID_ALGO]");
}
else if ((opt.list_options & LIST_SHOW_USAGE))
{
tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0));
}
if (pk->flags.primary && (opt.list_options & LIST_SHOW_OWNERTRUST))
{
tty_fprintf (fp, " [%s]", get_ownertrust_string (ctrl, pk, 0));
}
if (pk->flags.revoked)
{
tty_fprintf (fp, " [");
tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk));
tty_fprintf (fp, "]");
}
else if (pk->has_expired)
{
tty_fprintf (fp, " [");
tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk));
tty_fprintf (fp, "]");
}
else if (pk->expiredate)
{
tty_fprintf (fp, " [");
tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
tty_fprintf (fp, "]");
}
if (pk->pubkey_algo >= 100)
tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo);
tty_fprintf (fp, "\n");
if (pk->flags.primary && pk_is_disabled (pk))
es_fprintf (es_stdout, " *** %s\n", _("This key has been disabled"));
/* if the user hasn't explicitly asked for human-readable
fingerprints, show compact fpr of primary key: */
if (pk->flags.primary &&
!opt.fingerprint && !opt.with_fingerprint)
print_fingerprint (ctrl, fp, pk, 20);
/* Print the revocation reason. */
if (pk->flags.revoked)
print_revocation_reason (pk);
}
void
set_attrib_fd (int fd)
{
static int last_fd = -1;
if (fd != -1 && last_fd == fd)
return;
/* Fixme: Do we need to check for the log stream here? */
if (attrib_fp && attrib_fp != log_get_stream ())
es_fclose (attrib_fp);
attrib_fp = NULL;
if (fd == -1)
return;
if (! gnupg_fd_valid (fd))
log_fatal ("attribute-fd is invalid: %s\n", strerror (errno));
#ifdef HAVE_DOSISH_SYSTEM
setmode (fd, O_BINARY);
#endif
if (fd == 1)
attrib_fp = es_stdout;
else if (fd == 2)
attrib_fp = es_stderr;
else
attrib_fp = es_fdopen (fd, "wb");
if (!attrib_fp)
{
log_fatal ("can't open fd %d for attribute output: %s\n",
fd, strerror (errno));
}
last_fd = fd;
}
diff --git a/g10/main.h b/g10/main.h
index c0a3d5fa2..5d96b5e27 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -1,542 +1,543 @@
/* main.h
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2008, 2009, 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#ifndef G10_MAIN_H
#define G10_MAIN_H
#include "../common/types.h"
#include "../common/iobuf.h"
#include "../common/util.h"
#include "keydb.h"
#include "keyedit.h"
/* It could be argued that the default cipher should be 3DES rather
than AES128, and the default compression should be 0
(i.e. uncompressed) rather than 1 (zip). However, the real world
issues of speed and size come into play here. */
#if GPG_USE_AES256
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES256
#elif GPG_USE_AES128
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES
#elif GPG_USE_CAST5
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5
#else
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES
#endif
#define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1)
#define DEFAULT_S2K_DIGEST_ALGO DEFAULT_DIGEST_ALGO
#ifdef HAVE_ZIP
# define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP
#else
# define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE
#endif
#define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO)
/* Various data objects. */
typedef struct
{
ctrl_t ctrl;
int header_okay;
PK_LIST pk_list;
DEK *symkey_dek;
STRING2KEY *symkey_s2k;
cipher_filter_context_t cfx;
} encrypt_filter_context_t;
struct groupitem
{
char *name;
strlist_t values;
struct groupitem *next;
};
struct weakhash
{
enum gcry_md_algos algo;
int rejection_shown;
struct weakhash *next;
};
/*-- gpg.c --*/
extern int g10_errors_seen;
extern int assert_signer_true;
extern int assert_pubkey_algo_false;
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
void g10_exit(int rc) __attribute__ ((__noreturn__));
#else
void g10_exit(int rc);
#endif
void print_pubkey_algo_note (pubkey_algo_t algo);
void print_cipher_algo_note (cipher_algo_t algo);
void print_digest_algo_note (digest_algo_t algo);
void print_digest_rejected_note (enum gcry_md_algos algo);
void print_sha1_keysig_rejected_note (void);
void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec);
void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
void additional_weak_digest (const char* digestname);
int is_weak_digest (digest_algo_t algo);
/*-- armor.c --*/
char *make_radix64_string( const byte *data, size_t len );
/*-- misc.c --*/
void trap_unaligned(void);
void register_secured_file (const char *fname);
void unregister_secured_file (const char *fname);
int is_secured_file (gnupg_fd_t fd);
int is_secured_filename (const char *fname);
u16 checksum_u16( unsigned n );
u16 checksum( const byte *p, unsigned n );
u16 checksum_mpi( gcry_mpi_t a );
u32 buffer_to_u32( const byte *buffer );
const byte *get_session_marker( size_t *rlen );
enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo);
#define openpgp_cipher_open(_a,_b,_c,_d) \
gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d))
#define openpgp_cipher_get_algo_keylen(_a) \
gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a)))
#define openpgp_cipher_get_algo_blklen(_a) \
gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a)))
int openpgp_cipher_blocklen (cipher_algo_t algo);
int openpgp_cipher_test_algo(cipher_algo_t algo);
const char *openpgp_cipher_algo_name (cipher_algo_t algo);
const char *openpgp_cipher_algo_mode_name (cipher_algo_t algo,
aead_algo_t aead);
gpg_error_t openpgp_aead_test_algo (aead_algo_t algo);
const char *openpgp_aead_algo_name (aead_algo_t algo);
gpg_error_t openpgp_aead_algo_info (aead_algo_t algo,
enum gcry_cipher_modes *r_mode,
unsigned int *r_noncelen);
int openpgp_pk_test_algo (pubkey_algo_t algo);
int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use);
int openpgp_pk_algo_usage ( int algo );
const char *openpgp_pk_algo_name (pubkey_algo_t algo);
enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo);
int openpgp_md_test_algo (digest_algo_t algo);
const char *openpgp_md_algo_name (int algo);
struct expando_args
{
PKT_public_key *pk;
PKT_public_key *pksk;
byte imagetype;
int validity_info;
const char *validity_string;
const byte *namehash;
};
char *pct_expando (ctrl_t ctrl, const char *string,struct expando_args *args);
void deprecated_warning(const char *configname,unsigned int configlineno,
const char *option,const char *repl1,const char *repl2);
void deprecated_command (const char *name);
void obsolete_scdaemon_option (const char *configname,
unsigned int configlineno, const char *name);
int string_to_cipher_algo (const char *string);
aead_algo_t string_to_aead_algo (const char *string);
int string_to_digest_algo (const char *string);
const char *compress_algo_to_string(int algo);
int string_to_compress_algo(const char *string);
int check_compress_algo(int algo);
int default_cipher_algo(void);
int default_compress_algo(void);
void compliance_failure(void);
struct parse_options
{
char *name;
unsigned int bit;
char **value;
char *help;
};
char *optsep(char **stringp);
char *argsplit(char *string);
int parse_options(char *str,unsigned int *options,
struct parse_options *opts,int noisy);
const char *get_libexecdir (void);
int path_access(const char *file,int mode);
int pubkey_get_npkey (pubkey_algo_t algo);
int pubkey_get_nskey (pubkey_algo_t algo);
int pubkey_get_nsig (pubkey_algo_t algo);
int pubkey_get_nenc (pubkey_algo_t algo);
/* Temporary helpers. */
unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey );
int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
unsigned int ecdsa_qbits_from_Q (unsigned int qbits);
/*-- cpr.c --*/
void set_status_fd ( int fd );
int is_status_enabled ( void );
void write_status ( int no );
void write_status_error (const char *where, gpg_error_t err);
void write_status_errcode (const char *where, int errcode);
void write_status_failure (const char *where, gpg_error_t err);
void write_status_text ( int no, const char *text );
void write_status_printf (int no, const char *format,
...) GPGRT_ATTR_PRINTF(2,3);
void write_status_strings (int no, const char *text,
...) GPGRT_ATTR_SENTINEL(0);
gpg_error_t write_status_strings2 (ctrl_t dummy, int no,
...) GPGRT_ATTR_SENTINEL(0);
void write_status_buffer ( int no,
const char *buffer, size_t len, int wrap );
void write_status_text_and_buffer ( int no, const char *text,
const char *buffer, size_t len, int wrap );
void write_status_begin_signing (gcry_md_hd_t md);
int cpr_enabled(void);
char *cpr_get( const char *keyword, const char *prompt );
char *cpr_get_no_help( const char *keyword, const char *prompt );
char *cpr_get_utf8( const char *keyword, const char *prompt );
char *cpr_get_hidden( const char *keyword, const char *prompt );
void cpr_kill_prompt(void);
int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt,
int def_yes);
int cpr_get_answer_is_yes( const char *keyword, const char *prompt );
int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt );
int cpr_get_answer_okay_cancel (const char *keyword,
const char *prompt,
int def_answer);
/*-- helptext.c --*/
void display_online_help( const char *keyword );
/*-- encrypt.c --*/
gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek);
aead_algo_t use_aead (pk_list_t pk_list, int algo);
int use_mdc (pk_list_t pk_list,int algo);
int encrypt_symmetric (const char *filename );
int encrypt_store (const char *filename );
int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
gnupg_fd_t outputfd);
void encrypt_crypt_files (ctrl_t ctrl,
int nfiles, char **files, strlist_t remusr);
gpg_error_t reencrypt_to_new_recipients (ctrl_t ctrl, int armor,
const char *filename, iobuf_t infp,
strlist_t recipients,
DEK *dek,
struct pubkey_enc_list *pkenc_list);
int encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len);
int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid,
DEK *dek, iobuf_t out);
/*-- sign.c --*/
int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
int do_encrypt, strlist_t remusr, const char *outfile );
int clearsign_file (ctrl_t ctrl,
const char *fname, strlist_t locusr, const char *outfile);
int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr);
/*-- sig-check.c --*/
void sig_check_dump_stats (void);
/* SIG is a revocation signature. Check if any of PK's designated
revokers generated it. If so, return 0. Note: this function
(correctly) doesn't care if the designated revoker is revoked. */
int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig);
/* Check that the backsig BACKSIG from the subkey SUB_PK to its
primary key MAIN_PK is valid. */
int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk,
PKT_signature *backsig);
/* Check that the signature SIG over a key (e.g., a key binding or a
key revocation) is valid. (To check signatures over data, use
check_signature.) */
int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig,
int *is_selfsig );
/* Like check_key_signature, but with the ability to specify some
additional parameters and get back additional information. See the
documentation for the implementation for details. */
int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node,
PKT_public_key *check_pk, PKT_public_key *ret_pk,
int *is_selfsig, u32 *r_expiredate, int *r_expired);
/* Returns whether SIGNER generated the signature SIG over the packet
PACKET, which is a key, subkey or uid, and comes from the key block
KB. If SIGNER is NULL, it is looked up based on the information in
SIG. If not NULL, sets *IS_SELFSIG to indicate whether the
signature is a self-signature and *RET_PK to a copy of the signer's
key. */
gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl,
PKT_public_key *signer,
PKT_signature *sig,
KBNODE kb, PACKET *packet,
int *is_selfsig,
PKT_public_key *ret_pk);
/*-- delkey.c --*/
gpg_error_t delete_keys (ctrl_t ctrl,
strlist_t names, int secret, int allow_both);
/*-- keygen.c --*/
const char *get_default_pubkey_algo (void);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);
unsigned int ask_key_flags (int algo, int subkey, unsigned int current);
const char *ask_curve (int *algo, int *subkey_algo, const char *current);
void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr,
const char *usagestr, const char *expirestr);
void generate_keypair (ctrl_t ctrl, int full, const char *fname,
const char *card_serialno, int card_backup_key);
int keygen_set_std_prefs (const char *string,int personal);
PKT_user_id *keygen_get_std_prefs (void);
int keygen_add_key_expire( PKT_signature *sig, void *opaque );
int keygen_add_key_flags (PKT_signature *sig, void *opaque);
int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque);
int keygen_add_std_prefs( PKT_signature *sig, void *opaque );
int keygen_upd_std_prefs( PKT_signature *sig, void *opaque );
int keygen_add_keyserver_url(PKT_signature *sig, void *opaque);
int keygen_add_notations(PKT_signature *sig,void *opaque);
int keygen_add_revkey(PKT_signature *sig, void *opaque);
gpg_error_t make_backsig (ctrl_t ctrl,
PKT_signature *sig, PKT_public_key *pk,
PKT_public_key *sub_pk, PKT_public_key *sub_psk,
u32 timestamp, const char *cache_nonce);
void keygen_prepare_new_key_adsks (void);
gpg_error_t append_all_default_adsks (ctrl_t ctrl, kbnode_t pub_root);
gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock,
const char *algostr,
const char *usagestr,
const char *expirestr);
#ifdef ENABLE_CARD_SUPPORT
gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock,
int keyno, const char *serialno);
#endif
/*-- openfile.c --*/
int overwrite_filep( const char *fname );
char *make_outfile_name( const char *iname );
char *ask_outfile_name( const char *name, size_t namelen );
int open_outfile (gnupg_fd_t out_fd, const char *iname, int mode,
int restrictedperm, iobuf_t *a);
char *get_matching_datafile (const char *sigfilename);
iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx);
void try_make_homedir( const char *fname );
char *get_openpgp_revocdir (const char *home);
/*-- seskey.c --*/
void make_session_key( DEK *dek );
gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits );
gcry_mpi_t encode_md_value (PKT_public_key *pk,
gcry_md_hd_t md, int hash_algo );
/*-- import.c --*/
struct import_stats_s;
typedef struct import_stats_s *import_stats_t;
struct import_filter_s;
typedef struct import_filter_s *import_filter_t;
typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg);
int parse_import_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_import_filter (const char *string);
import_filter_t save_and_clear_import_filter (void);
void restore_import_filter (import_filter_t filt);
gpg_error_t read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname,
const void *buffer, size_t buflen,
kbnode_t *r_keyblock);
gpg_error_t import_included_key_block (ctrl_t ctrl, kbnode_t keyblock);
void import_keys (ctrl_t ctrl, char **fnames, int nnames,
import_stats_t stats_hd, unsigned int options,
int origin, const char *url);
gpg_error_t import_keys_es_stream (ctrl_t ctrl, estream_t fp,
import_stats_t stats_handle,
unsigned char **fpr, size_t *fpr_len,
unsigned int options,
import_screener_t screener, void *screener_arg,
int origin, const char *url);
gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname);
import_stats_t import_new_stats_handle (void);
void import_release_stats_handle (import_stats_t hd);
void import_print_stats (import_stats_t hd);
/* Communication for impex_filter_getval */
struct impex_filter_parm_s
{
ctrl_t ctrl;
kbnode_t node;
char hexfpr[2*MAX_FINGERPRINT_LEN + 1];
};
const char *impex_filter_getval (void *cookie, const char *propname);
gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats,
kbnode_t sec_keyblock, int batch, int force,
int only_marked);
int collapse_uids (kbnode_t *keyblock);
int collapse_subkeys (kbnode_t *keyblock);
const char *revocation_reason_code_to_str (int code, char **r_freeme);
int get_revocation_reason (PKT_signature *sig, char **r_reason,
char **r_comment, size_t *r_commentlen);
/*-- export.c --*/
struct export_stats_s;
typedef struct export_stats_s *export_stats_t;
export_stats_t export_new_stats (void);
void export_release_stats (export_stats_t stats);
void export_print_stats (export_stats_t stats);
int parse_export_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_export_filter (const char *string);
void push_export_filters (void);
void pop_export_filters (void);
int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node);
int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
unsigned int options,
const void *prefix, size_t prefixlen,
export_stats_t stats,
kbnode_t *r_keyblock,
void **r_data, size_t *r_datalen);
gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
int cleartext, int mode1003,
char **cache_nonce_addr,
const char *hexgrip,
PKT_public_key *pk, gcry_sexp_t *r_key);
gpg_error_t write_keyblock_to_output (kbnode_t keyblock,
int with_armor, unsigned int options);
gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid);
gpg_error_t export_secret_ssh_key (ctrl_t ctrl, const char *userid);
/*-- dearmor.c --*/
int dearmor_file( const char *fname );
int enarmor_file( const char *fname );
/*-- revoke.c --*/
struct revocation_reason_info;
int gen_standard_revoke (ctrl_t ctrl,
PKT_public_key *psk, const char *cache_nonce);
int gen_revoke (ctrl_t ctrl, const char *uname);
int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr);
int revocation_reason_build_cb( PKT_signature *sig, void *opaque );
struct revocation_reason_info *
ask_revocation_reason( int key_rev, int cert_rev, int hint );
struct revocation_reason_info * get_default_uid_revocation_reason (void);
struct revocation_reason_info * get_default_sig_revocation_reason (void);
void release_revocation_reason_info (struct revocation_reason_info *reason);
/*-- keylist.c --*/
void public_key_list (ctrl_t ctrl, strlist_t list,
int locate_mode, int no_local);
void secret_key_list (ctrl_t ctrl, strlist_t list );
gpg_error_t parse_and_set_list_filter (const char *string);
void print_subpackets_colon(PKT_signature *sig);
void reorder_keyblock (KBNODE keyblock);
gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret,
int has_secret, int fpr, int no_validity);
int cmp_signodes (const void *av, const void *bv);
void print_fingerprint (ctrl_t ctrl, estream_t fp,
PKT_public_key *pk, int mode);
void print_revokers (estream_t fp, int colon_mode, PKT_public_key *pk);
void show_preferences (PKT_user_id *uid, int indent, int mode, int verbose);
void show_policy_url(PKT_signature *sig,int indent,int mode);
void show_keyserver_url(PKT_signature *sig,int indent,int mode);
void show_notation(PKT_signature *sig,int indent,int mode,int which);
+void print_matching_notations (PKT_signature *sig);
void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk);
void set_attrib_fd(int fd);
void print_key_info (ctrl_t ctrl, estream_t fp, int indent,
PKT_public_key *pk, int secret);
void print_key_info_log (ctrl_t ctrl, int loglevel, int indent,
PKT_public_key *pk, int secret);
void print_card_key_info (estream_t fp, KBNODE keyblock);
void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret);
void print_revocation_reason_comment (const char *comment, size_t comment_len);
/*-- verify.c --*/
void print_file_status( int status, const char *name, int what );
int verify_signatures (ctrl_t ctrl, int nfiles, char **files );
int verify_files (ctrl_t ctrl, int nfiles, char **files );
int gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd,
estream_t out_fp);
void check_assert_signer_list (const char *mainpkhex, const char *pkhex);
void check_assert_pubkey_algo (const char *algostr, const char *pkhex);
/*-- decrypt.c --*/
gpg_error_t decrypt_message (ctrl_t ctrl, const char *filename,
strlist_t remusr);
gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
gnupg_fd_t output_fd);
void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]);
/*-- plaintext.c --*/
int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2,
strlist_t files, const char *sigfilename, int textmode);
int hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2,
gnupg_fd_t data_fd, int textmode);
PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf);
/*-- server.c --*/
int gpg_server (ctrl_t);
gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl,
const unsigned char *line);
#ifdef ENABLE_CARD_SUPPORT
/*-- card-util.c --*/
void change_pin (int no, int allow_admin);
void card_status (ctrl_t ctrl, estream_t fp, const char *serialno);
void card_edit (ctrl_t ctrl, strlist_t commands);
gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock);
int card_store_subkey (KBNODE node, int use, strlist_t *processed_keys);
#endif
/*-- migrate.c --*/
void migrate_secring (ctrl_t ctrl);
#endif /*G10_MAIN_H*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 22d12799d..5a7961099 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -1,2918 +1,2919 @@
/* mainproc.c - handle packets
* Copyright (C) 1998-2009 Free Software Foundation, Inc.
* Copyright (C) 2013-2014 Werner Koch
* Copyright (C) 2020, 2024 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/iobuf.h"
#include "options.h"
#include "keydb.h"
#include "filter.h"
#include "main.h"
#include "../common/status.h"
#include "../common/i18n.h"
#include "trustdb.h"
#include "keyserver-internal.h"
#include "photoid.h"
#include "../common/mbox-util.h"
#include "call-dirmngr.h"
#include "../common/compliance.h"
/* Put an upper limit on nested packets. The 32 is an arbitrary
value, a much lower should actually be sufficient. */
#define MAX_NESTING_DEPTH 32
/* An object to build a list of symkey packet info. */
struct symlist_item
{
struct symlist_item *next;
int cipher_algo;
int cfb_mode;
int other_error;
};
/*
* Object to hold the processing context.
*/
typedef struct mainproc_context *CTX;
struct mainproc_context
{
ctrl_t ctrl;
struct mainproc_context *anchor; /* May be useful in the future. */
PKT_public_key *last_pubkey;
PKT_user_id *last_user_id;
md_filter_context_t mfx;
int sigs_only; /* Process only signatures and reject all other stuff. */
int encrypt_only; /* Process only encryption messages. */
/* Name of the file with the complete signature or the file with the
detached signature. This is currently only used to deduce the
file name of the data file if that has not been given. */
const char *sigfilename;
/* A structure to describe the signed data in case of a detached
signature. */
struct
{
/* A file descriptor of the signed data. Only used if not -1. */
gnupg_fd_t data_fd;
/* A list of filenames with the data files or NULL. This is only
used if DATA_FD is -1. */
strlist_t data_names;
/* Flag to indicated that either one of the next previous fields
is used. This is only needed for better readability. */
int used;
} signed_data;
DEK *dek;
int last_was_session_key;
kbnode_t list; /* The current list of packets. */
iobuf_t iobuf; /* Used to get the filename etc. */
int trustletter; /* Temporary usage in list_node. */
ulong symkeys; /* Number of symmetrically encrypted session keys. */
struct pubkey_enc_list *pkenc_list; /* List of encryption packets. */
struct symlist_item *symenc_list; /* List of sym. encryption packets. */
int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD packet seen. */
int seen_pkt_encrypted_mdc; /* PKT_ENCRYPTED_MDC packet seen. */
struct {
unsigned int sig_seen:1; /* Set to true if a signature packet
has been seen. */
unsigned int data:1; /* Any data packet seen */
unsigned int uncompress_failed:1;
} any;
};
/* Counter with the number of literal data packets seen. Note that
* this is also bumped at the end of an encryption. This counter is
* used for a basic consistency check of a received PGP message. */
static int literals_seen;
/*** Local prototypes. ***/
static int do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list);
static void list_node (CTX c, kbnode_t node);
static void proc_tree (CTX c, kbnode_t node);
/*** Functions. ***/
/* Reset the literal data counter. This is required to setup a new
* decryption or verification context. */
void
reset_literals_seen(void)
{
literals_seen = 0;
}
static void
release_list( CTX c )
{
proc_tree (c, c->list);
release_kbnode (c->list);
while (c->pkenc_list)
{
struct pubkey_enc_list *tmp = c->pkenc_list->next;
release_pubkey_enc_parts (&c->pkenc_list->d);
xfree (c->pkenc_list);
c->pkenc_list = tmp;
}
c->pkenc_list = NULL;
while (c->symenc_list)
{
struct symlist_item *tmp = c->symenc_list->next;
xfree (c->symenc_list);
c->symenc_list = tmp;
}
c->symenc_list = NULL;
c->list = NULL;
c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0;
c->seen_pkt_encrypted_aead = 0;
c->seen_pkt_encrypted_mdc = 0;
xfree (c->dek);
c->dek = NULL;
}
static int
add_onepass_sig (CTX c, PACKET *pkt)
{
kbnode_t node;
if (c->list) /* Add another packet. */
add_kbnode (c->list, new_kbnode (pkt));
else /* Insert the first one. */
c->list = node = new_kbnode (pkt);
return 1;
}
static int
add_gpg_control (CTX c, PACKET *pkt)
{
if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
{
/* New clear text signature.
* Process the last one and reset everything */
release_list(c);
}
if (c->list) /* Add another packet. */
add_kbnode (c->list, new_kbnode (pkt));
else /* Insert the first one. */
c->list = new_kbnode (pkt);
return 1;
}
static int
add_user_id (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("orphaned user ID\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_subkey (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("subkey w/o mainkey\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_ring_trust (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("ring trust w/o key\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_signature (CTX c, PACKET *pkt)
{
kbnode_t node;
c->any.sig_seen = 1;
if (pkt->pkttype == PKT_SIGNATURE && !c->list)
{
/* This is the first signature for the following datafile.
* GPG does not write such packets; instead it always uses
* onepass-sig packets. The drawback of PGP's method
* of prepending the signature to the data is
* that it is not possible to make a signature from data read
* from stdin. (GPG is able to read PGP stuff anyway.) */
node = new_kbnode (pkt);
c->list = node;
return 1;
}
else if (!c->list)
return 0; /* oops (invalid packet sequence)*/
else if (!c->list->pkt)
BUG(); /* so nicht */
/* Add a new signature node item at the end. */
node = new_kbnode (pkt);
add_kbnode (c->list, node);
return 1;
}
static gpg_error_t
symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
{
gpg_error_t err;
gcry_cipher_hd_t hd;
unsigned int noncelen, keylen;
enum gcry_cipher_modes ciphermode;
if (dek->use_aead)
{
err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
if (err)
return err;
}
else
{
ciphermode = GCRY_CIPHER_MODE_CFB;
noncelen = 0;
}
/* Check that the session key has a size of 16 to 32 bytes. */
if ((dek->use_aead && (slen < (noncelen + 16 + 16)
|| slen > (noncelen + 32 + 16)))
|| (!dek->use_aead && (slen < 17 || slen > 33)))
{
log_error ( _("weird size for an encrypted session key (%d)\n"),
(int)slen);
return gpg_error (GPG_ERR_BAD_KEY);
}
err = openpgp_cipher_open (&hd, dek->algo, ciphermode, GCRY_CIPHER_SECURE);
if (!err)
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (!err)
err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen);
if (err)
goto leave;
if (dek->use_aead)
{
byte ad[4];
ad[0] = (0xc0 | PKT_SYMKEY_ENC);
ad[1] = 5;
ad[2] = dek->algo;
ad[3] = dek->use_aead;
err = gcry_cipher_authenticate (hd, ad, 4);
if (err)
goto leave;
gcry_cipher_final (hd);
keylen = slen - noncelen - 16;
err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
if (err)
goto leave;
err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
if (err)
goto leave;
/* Now we replace the dek components with the real session key to
* decrypt the contents of the sequencing packet. */
if (keylen > DIM(dek->key))
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
dek->keylen = keylen;
memcpy (dek->key, seskey + noncelen, dek->keylen);
}
else
{
gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 );
/* Here we can only test whether the algo given in decrypted
* session key is a valid OpenPGP algo. With 11 defined
* symmetric algorithms we will miss 4.3% of wrong passphrases
* here. The actual checking is done later during bulk
* decryption; we can't bring this check forward easily. We
* need to use the GPG_ERR_CHECKSUM so that we won't run into
* the gnupg < 2.2 bug compatible case which would terminate the
* process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above)
* we will have a reliable test here. */
if (openpgp_cipher_test_algo (seskey[0])
|| openpgp_cipher_get_algo_keylen (seskey[0]) != slen - 1)
{
err = gpg_error (GPG_ERR_CHECKSUM);
goto leave;
}
/* Now we replace the dek components with the real session key to
* decrypt the contents of the sequencing packet. */
keylen = slen-1;
if (keylen > DIM(dek->key))
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
dek->algo = seskey[0];
dek->keylen = keylen;
memcpy (dek->key, seskey + 1, dek->keylen);
}
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
leave:
gcry_cipher_close (hd);
return err;
}
static void
proc_symkey_enc (CTX c, PACKET *pkt)
{
gpg_error_t err;
PKT_symkey_enc *enc;
enc = pkt->pkt.symkey_enc;
if (!enc)
log_error ("invalid symkey encrypted packet\n");
else if(!c->dek)
{
int algo = enc->cipher_algo;
const char *s = openpgp_cipher_algo_name (algo);
const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo)
/**/ : "CFB");
if (!openpgp_cipher_test_algo (algo))
{
if (!opt.quiet)
{
if (enc->seskeylen)
log_info (_("%s.%s encrypted session key\n"), s, a );
else
log_info (_("%s.%s encrypted data\n"), s, a );
}
}
else
{
log_error (_("encrypted with unknown algorithm %d.%s\n"), algo, a);
s = NULL; /* Force a goto leave. */
}
if (openpgp_md_test_algo (enc->s2k.hash_algo))
{
log_error(_("passphrase generated with unknown digest"
" algorithm %d\n"),enc->s2k.hash_algo);
s = NULL;
}
c->last_was_session_key = 2;
if (!s || opt.list_only)
goto leave;
if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
if (get_override_session_key (c->dek, opt.override_session_key))
{
xfree (c->dek);
c->dek = NULL;
}
}
else
{
c->dek = passphrase_to_dek (algo, &enc->s2k, 0, 0, NULL,
GETPASSWORD_FLAG_SYMDECRYPT, NULL);
if (c->dek)
{
c->dek->symmetric = 1;
c->dek->use_aead = enc->aead_algo;
/* FIXME: This doesn't work perfectly if a symmetric key
comes before a public key in the message - if the
user doesn't know the passphrase, then there is a
chance that the "decrypted" algorithm will happen to
be a valid one, which will make the returned dek
appear valid, so we won't try any public keys that
come later. */
if (enc->seskeylen)
{
err = symkey_decrypt_seskey (c->dek,
enc->seskey, enc->seskeylen);
if (err)
{
log_info ("decryption of the symmetrically encrypted"
" session key failed: %s\n",
gpg_strerror (err));
if (gpg_err_code (err) != GPG_ERR_BAD_KEY
&& gpg_err_code (err) != GPG_ERR_CHECKSUM)
log_fatal ("process terminated to be bug compatible"
" with GnuPG <= 2.2\n");
else
write_status_text (STATUS_ERROR,
"symkey_decrypt.maybe_error"
" 11_BAD_PASSPHRASE");
if (c->dek->s2k_cacheid[0])
{
if (opt.debug)
log_debug ("cleared passphrase cached with ID:"
" %s\n", c->dek->s2k_cacheid);
passphrase_clear_cache (c->dek->s2k_cacheid);
}
xfree (c->dek);
c->dek = NULL;
}
}
else
c->dek->algo_info_printed = 1;
}
}
}
leave:
/* Record infos from the packet. */
{
struct symlist_item *symitem;
symitem = xcalloc (1, sizeof *symitem);
if (enc)
{
symitem->cipher_algo = enc->cipher_algo;
symitem->cfb_mode = !enc->aead_algo;
}
else
symitem->other_error = 1;
symitem->next = c->symenc_list;
c->symenc_list = symitem;
}
c->symkeys++;
free_packet (pkt, NULL);
}
static void
proc_pubkey_enc (CTX c, PACKET *pkt)
{
PKT_pubkey_enc *enc;
/* Check whether the secret key is available and store in this case. */
c->last_was_session_key = 1;
enc = pkt->pkt.pubkey_enc;
/*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/
/* Hmmm: why do I have this algo check here - anyway there is
* function to check it. */
if (opt.verbose)
log_info (_("public key is %s\n"), keystr (enc->keyid));
if (is_status_enabled ())
{
char buf[50];
snprintf (buf, sizeof buf, "%08lX%08lX %d 0",
(ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo);
write_status_text (STATUS_ENC_TO, buf);
}
if (!opt.list_only && !opt.override_session_key)
{
struct pubkey_enc_list *x = xcalloc (1, sizeof *x);
copy_pubkey_enc_parts (&x->d, enc);
x->result = -1;
x->next = c->pkenc_list;
c->pkenc_list = x;
}
free_packet(pkt, NULL);
}
/*
* Print the list of public key encrypted packets which we could
* not decrypt.
*/
static void
print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list)
{
for (; list; list = list->next)
{
PKT_public_key *pk;
char pkstrbuf[PUBKEY_STRING_SIZE];
char *p;
pk = xmalloc_clear (sizeof *pk);
pk->pubkey_algo = list->d.pubkey_algo;
if (!get_pubkey (ctrl, pk, list->d.keyid))
{
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
log_info (_("encrypted with %s key, ID %s, created %s\n"),
pkstrbuf, keystr_from_pk (pk),
strtimestamp (pk->timestamp));
p = get_user_id_native (ctrl, list->d.keyid);
log_printf (_(" \"%s\"\n"), p);
xfree (p);
}
else
log_info (_("encrypted with %s key, ID %s\n"),
openpgp_pk_algo_name (list->d.pubkey_algo),
keystr (list->d.keyid));
if (opt.flags.require_pqc_encryption
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
log_info (_("WARNING: key is not quantum-resistant\n"));
free_public_key (pk);
}
}
static void
proc_encrypted (CTX c, PACKET *pkt)
{
int result = 0;
int early_plaintext = literals_seen;
unsigned int compliance_de_vs = 0;
if (pkt)
{
if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
c->seen_pkt_encrypted_aead = 1;
if (pkt->pkttype == PKT_ENCRYPTED_MDC)
c->seen_pkt_encrypted_mdc = 1;
}
else /* No PKT indicates the the add-recipients mode. */
log_assert (c->ctrl->modify_recipients);
if (early_plaintext)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA);
/* We fail only later so that we can print some more info first. */
}
if (!opt.quiet)
{
if (c->symkeys>1)
log_info (_("encrypted with %lu passphrases\n"), c->symkeys);
else if (c->symkeys == 1)
log_info (_("encrypted with 1 passphrase\n"));
print_pkenc_list (c->ctrl, c->pkenc_list);
}
/* Figure out the session key by looking at all pkenc packets. */
if (opt.list_only || c->dek)
;
else if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
result = get_override_session_key (c->dek, opt.override_session_key);
if (result)
{
xfree (c->dek);
c->dek = NULL;
log_info (_("public key decryption failed: %s\n"),
gpg_strerror (result));
write_status_error ("pkdecrypt_failed", result);
}
}
else if (c->pkenc_list)
{
c->dek = xmalloc_secure_clear (sizeof *c->dek);
result = get_session_key (c->ctrl, c->pkenc_list, c->dek);
if (is_status_enabled ())
{
struct pubkey_enc_list *list;
for (list = c->pkenc_list; list; list = list->next)
if (list->result)
{ /* Key was not tried or it caused an error. */
char buf[20];
snprintf (buf, sizeof buf, "%08lX%08lX",
(ulong)list->d.keyid[0], (ulong)list->d.keyid[1]);
write_status_text (STATUS_NO_SECKEY, buf);
}
}
if (result)
{
log_info (_("public key decryption failed: %s\n"),
gpg_strerror (result));
write_status_error ("pkdecrypt_failed", result);
/* Error: Delete the DEK. */
xfree (c->dek);
c->dek = NULL;
}
}
if (c->dek && opt.verbose > 1)
log_info (_("public key encrypted data: good DEK\n"));
if (c->ctrl->modify_recipients)
{
if (c->anchor)
{
log_error ("command not possible with nested data\n");
write_status_errcode ("decryption.mod_recp", GPG_ERR_BAD_DATA);
xfree (c->dek);
c->dek = NULL;
return;
}
literals_seen++;
/* Simply return here. Our caller will then test for DEK and
* the PK_list to decide whether decryption worked. */
return;
}
if (!opt.show_only_session_key)
write_status (STATUS_BEGIN_DECRYPTION);
/*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/
if (opt.list_only)
result = -1;
else if (!c->dek && !c->last_was_session_key)
{
int algo;
STRING2KEY s2kbuf;
STRING2KEY *s2k = NULL;
int canceled;
if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
result = get_override_session_key (c->dek, opt.override_session_key);
if (result)
{
xfree (c->dek);
c->dek = NULL;
}
}
else
{
/* Assume this is old style conventional encrypted data. */
algo = opt.def_cipher_algo;
if (algo)
log_info (_("assuming %s encrypted data\n"),
openpgp_cipher_algo_name (algo));
else if (openpgp_cipher_test_algo (CIPHER_ALGO_IDEA))
{
algo = opt.def_cipher_algo;
if (!algo)
algo = opt.s2k_cipher_algo;
log_info (_("IDEA cipher unavailable, "
"optimistically attempting to use %s instead\n"),
openpgp_cipher_algo_name (algo));
}
else
{
algo = CIPHER_ALGO_IDEA;
if (!opt.s2k_digest_algo)
{
/* If no digest is given we assume SHA-1. */
s2kbuf.mode = 0;
s2kbuf.hash_algo = DIGEST_ALGO_SHA1;
s2k = &s2kbuf;
}
log_info (_("assuming %s encrypted data\n"), "IDEA");
}
c->dek = passphrase_to_dek (algo, s2k, 0, 0, NULL,
GETPASSWORD_FLAG_SYMDECRYPT, &canceled);
if (c->dek)
c->dek->algo_info_printed = 1;
else if (canceled)
result = gpg_error (GPG_ERR_CANCELED);
else
result = gpg_error (GPG_ERR_INV_PASSPHRASE);
}
}
else if (!c->dek)
{
if (c->symkeys && !c->pkenc_list)
result = gpg_error (GPG_ERR_BAD_KEY);
if (!result)
result = gpg_error (GPG_ERR_NO_SECKEY);
}
/* Compute compliance with CO_DE_VS. */
if (!result && (is_status_enabled () || opt.flags.require_compliance)
/* Overriding session key voids compliance. */
&& !opt.override_session_key
/* Check symmetric cipher. */
&& gnupg_gcrypt_is_compliant (CO_DE_VS)
&& gnupg_cipher_is_compliant (CO_DE_VS, c->dek->algo,
GCRY_CIPHER_MODE_CFB))
{
struct pubkey_enc_list *i;
struct symlist_item *si;
int compliant = 1;
PKT_public_key *pk = xmalloc (sizeof *pk);
if ( !(c->pkenc_list || c->symkeys) )
log_debug ("%s: where else did the session key come from?\n", __func__);
/* Check that all seen symmetric key packets use compliant
* algos. This is so that no non-compliant encrypted session
* key can be sneaked in. */
for (si = c->symenc_list; si && compliant; si = si->next)
{
if (!si->cfb_mode
|| !gnupg_cipher_is_compliant (CO_DE_VS, si->cipher_algo,
GCRY_CIPHER_MODE_CFB))
compliant = 0;
}
/* Check that every known public key used to encrypt the session key
* is compliant. */
for (i = c->pkenc_list; i && compliant; i = i->next)
{
memset (pk, 0, sizeof *pk);
pk->pubkey_algo = i->d.pubkey_algo;
if (!get_pubkey (c->ctrl, pk, i->d.keyid)
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
compliant = 0;
release_public_key_parts (pk);
}
xfree (pk);
if (compliant)
compliance_de_vs |= 1;
}
if (!result)
{
int compl_error;
result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek,
&compl_error);
if (!result && !compl_error)
compliance_de_vs |= 2;
}
/* Trigger the deferred error. */
if (!result && early_plaintext)
result = gpg_error (GPG_ERR_BAD_DATA);
else if (!result && opt.show_only_session_key)
result = -1;
if (result == -1)
;
else if (!result
&& !opt.ignore_mdc_error
&& !pkt->pkt.encrypted->mdc_method
&& !pkt->pkt.encrypted->aead_algo)
{
/* The message has been decrypted but does not carry an MDC or
* uses AEAD encryption. --ignore-mdc-error has also not been
* used. To avoid attacks changing an MDC message to a non-MDC
* message, we fail here. */
log_error (_("WARNING: message was not integrity protected\n"));
if (!pkt->pkt.encrypted->mdc_method
&& (openpgp_cipher_get_algo_blklen (c->dek->algo) == 8
|| c->dek->algo == CIPHER_ALGO_TWOFISH))
{
/* Before 2.2.8 we did not fail hard for a missing MDC if
* one of the old ciphers where used. Although these cases
* are rare in practice we print a hint on how to decrypt
* such messages. */
log_string
(GPGRT_LOGLVL_INFO,
_("Hint: If this message was created before the year 2003 it is\n"
"likely that this message is legitimate. This is because back\n"
"then integrity protection was not widely used.\n"));
log_info (_("Use the option '%s' to decrypt anyway.\n"),
"--ignore-mdc-error");
write_status_errcode ("nomdc_with_legacy_cipher",
GPG_ERR_DECRYPT_FAILED);
}
log_info (_("decryption forced to fail!\n"));
write_status (STATUS_DECRYPTION_FAILED);
}
else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
&& !pkt->pkt.encrypted->aead_algo
&& opt.ignore_mdc_error))
{
/* All is fine or for an MDC message the MDC failed but the
* --ignore-mdc-error option is active. For compatibility
* reasons we issue GOODMDC also for AEAD messages. */
write_status (STATUS_DECRYPTION_OKAY);
if (opt.verbose > 1)
log_info(_("decryption okay\n"));
if (pkt->pkt.encrypted->aead_algo)
{
write_status (STATUS_GOODMDC);
compliance_de_vs |= 4;
}
else if (pkt->pkt.encrypted->mdc_method && !result)
{
write_status (STATUS_GOODMDC);
compliance_de_vs |= 4;
}
else
log_info (_("WARNING: message was not integrity protected\n"));
}
else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
|| gpg_err_code (result) == GPG_ERR_TRUNCATED)
{
glo_ctrl.lasterr = result;
log_error (_("WARNING: encrypted message has been manipulated!\n"));
write_status (STATUS_BADMDC);
write_status (STATUS_DECRYPTION_FAILED);
}
else
{
if (gpg_err_code (result) == GPG_ERR_BAD_KEY
|| gpg_err_code (result) == GPG_ERR_CHECKSUM
|| gpg_err_code (result) == GPG_ERR_CIPHER_ALGO)
{
if (c->symkeys)
write_status_text (STATUS_ERROR,
"symkey_decrypt.maybe_error"
" 11_BAD_PASSPHRASE");
if (c->dek && *c->dek->s2k_cacheid != '\0')
{
if (opt.debug)
log_debug ("cleared passphrase cached with ID: %s\n",
c->dek->s2k_cacheid);
passphrase_clear_cache (c->dek->s2k_cacheid);
}
}
glo_ctrl.lasterr = result;
write_status (STATUS_DECRYPTION_FAILED);
log_error (_("decryption failed: %s\n"), gpg_strerror (result));
/* Hmmm: does this work when we have encrypted using multiple
* ways to specify the session key (symmmetric and PK). */
}
/* If we concluded that the decryption was compliant, issue a
* compliance status before the end of the decryption status. */
if (compliance_de_vs == (4|2|1))
{
write_status_strings (STATUS_DECRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
}
xfree (c->dek);
c->dek = NULL;
free_packet (pkt, NULL);
c->last_was_session_key = 0;
if (!opt.show_only_session_key)
write_status (STATUS_END_DECRYPTION);
/* Bump the counter even if we have not seen a literal data packet
* inside an encryption container. This acts as a sentinel in case
* a misplace extra literal data packets follows after this
* encrypted packet. */
literals_seen++;
/* The --require-compliance option allows one to simplify decryption in
* de-vs compliance mode by just looking at the exit status. */
if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS
&& compliance_de_vs != (4|2|1)
&& !opt.show_only_session_key)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
g10_errors_seen = 1;
}
}
static int
have_seen_pkt_encrypted_aead_or_mdc( CTX c )
{
CTX cc;
for (cc = c; cc; cc = cc->anchor)
{
if (cc->seen_pkt_encrypted_aead)
return 1;
if (cc->seen_pkt_encrypted_mdc)
return 1;
}
return 0;
}
static void
proc_plaintext( CTX c, PACKET *pkt )
{
PKT_plaintext *pt = pkt->pkt.plaintext;
int any, clearsig, rc;
kbnode_t n;
unsigned char *extrahash;
size_t extrahashlen;
/* This is a literal data packet. Bump a counter for later checks. */
literals_seen++;
if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8))
log_info (_("Note: sender requested \"for-your-eyes-only\"\n"));
else if (opt.verbose)
{
/* We don't use print_utf8_buffer because that would require a
* string change which we don't want in 2.2. It is also not
* clear whether the filename is always utf-8 encoded. */
char *tmp = make_printable_string (pt->name, pt->namelen, 0);
log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp);
xfree (tmp);
}
free_md_filter_context (&c->mfx);
if (gcry_md_open (&c->mfx.md, 0, 0))
BUG ();
/* fixme: we may need to push the textfilter if we have sigclass 1
* and no armoring - Not yet tested
* Hmmm, why don't we need it at all if we have sigclass 1
* Should we assume that plaintext in mode 't' has always sigclass 1??
* See: Russ Allbery's mail 1999-02-09
*/
any = clearsig = 0;
for (n=c->list; n; n = n->next )
{
if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* The onepass signature case. */
if (n->pkt->pkt.onepass_sig->digest_algo)
{
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md,
n->pkt->pkt.onepass_sig->digest_algo);
any = 1;
}
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START)
{
/* The clearsigned message case. */
size_t datalen = n->pkt->pkt.gpg_control->datalen;
const byte *data = n->pkt->pkt.gpg_control->data;
/* Check that we have at least the sigclass and one hash. */
if (datalen < 2)
log_fatal ("invalid control packet CTRLPKT_CLEARSIGN_START\n");
/* Note that we don't set the clearsig flag for not-dash-escaped
* documents. */
clearsig = (*data == 0x01);
for (data++, datalen--; datalen; datalen--, data++)
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md, *data);
any = 1;
break; /* Stop here as one-pass signature packets are not
expected. */
}
else if (n->pkt->pkttype == PKT_SIGNATURE)
{
/* The SIG+LITERAL case that PGP used to use. */
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md, n->pkt->pkt.signature->digest_algo);
any = 1;
}
}
if (!any && !opt.skip_verify && !have_seen_pkt_encrypted_aead_or_mdc(c))
{
/* This is for the old GPG LITERAL+SIG case. It's not legal
according to 2440, so hopefully it won't come up that often.
There is no good way to specify what algorithms to use in
that case, so these there are the historical answer. */
gcry_md_enable (c->mfx.md, DIGEST_ALGO_RMD160);
gcry_md_enable (c->mfx.md, DIGEST_ALGO_SHA1);
}
if (DBG_HASHING)
{
gcry_md_debug (c->mfx.md, "verify");
if (c->mfx.md2)
gcry_md_debug (c->mfx.md2, "verify2");
}
rc=0;
if (literals_seen > 1)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
write_status_text (STATUS_ERROR, "proc_pkt.plaintext 89_BAD_DATA");
log_inc_errorcount ();
rc = gpg_error (GPG_ERR_UNEXPECTED);
}
if (!rc)
{
/* It we are in --verify mode, we do not want to output the
* signed text. However, if --output is also used we do what
* has been requested and write out the signed data. */
rc = handle_plaintext (pt, &c->mfx,
(opt.outfp || opt.outfile)? 0 : c->sigs_only,
clearsig);
if (gpg_err_code (rc) == GPG_ERR_EACCES && !c->sigs_only)
{
/* Can't write output but we hash it anyway to check the
signature. */
rc = handle_plaintext( pt, &c->mfx, 1, clearsig );
}
}
if (rc)
log_error ("handle plaintext failed: %s\n", gpg_strerror (rc));
/* We add a marker control packet instead of the plaintext packet.
* This is so that we can later detect invalid packet sequences.
* The packet is further used to convey extra data from the
* plaintext packet to the signature verification. */
extrahash = xtrymalloc (6 + pt->namelen);
if (!extrahash)
{
/* No way to return an error. */
rc = gpg_error_from_syserror ();
log_error ("malloc failed in %s: %s\n", __func__, gpg_strerror (rc));
extrahashlen = 0;
}
else
{
extrahash[0] = pt->mode;
extrahash[1] = pt->namelen;
if (pt->namelen)
memcpy (extrahash+2, pt->name, pt->namelen);
extrahashlen = 2 + pt->namelen;
extrahash[extrahashlen++] = pt->timestamp >> 24;
extrahash[extrahashlen++] = pt->timestamp >> 16;
extrahash[extrahashlen++] = pt->timestamp >> 8;
extrahash[extrahashlen++] = pt->timestamp ;
}
free_packet (pkt, NULL);
c->last_was_session_key = 0;
n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK,
extrahash, extrahashlen));
xfree (extrahash);
if (c->list)
add_kbnode (c->list, n);
else
c->list = n;
}
static int
proc_compressed_cb (iobuf_t a, void *info)
{
if ( ((CTX)info)->signed_data.used
&& ((CTX)info)->signed_data.data_fd != GNUPG_INVALID_FD)
return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a,
((CTX)info)->signed_data.data_fd);
else
return proc_signature_packets (((CTX)info)->ctrl, info, a,
((CTX)info)->signed_data.data_names,
((CTX)info)->sigfilename );
}
static int
proc_encrypt_cb (iobuf_t a, void *info )
{
CTX c = info;
return proc_encryption_packets (c->ctrl, info, a, NULL, NULL);
}
static int
proc_compressed (CTX c, PACKET *pkt)
{
PKT_compressed *zd = pkt->pkt.compressed;
int rc;
/*printf("zip: compressed data packet\n");*/
if (c->sigs_only)
rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c);
else if( c->encrypt_only )
rc = handle_compressed (c->ctrl, c, zd, proc_encrypt_cb, c);
else
rc = handle_compressed (c->ctrl, c, zd, NULL, NULL);
if (gpg_err_code (rc) == GPG_ERR_BAD_DATA)
{
if (!c->any.uncompress_failed)
{
CTX cc;
for (cc=c; cc; cc = cc->anchor)
cc->any.uncompress_failed = 1;
log_error ("uncompressing failed: %s\n", gpg_strerror (rc));
}
}
else if (rc)
log_error ("uncompressing failed: %s\n", gpg_strerror (rc));
free_packet (pkt, NULL);
c->last_was_session_key = 0;
return rc;
}
/*
* Check the signature. If R_PK is not NULL a copy of the public key
* used to verify the signature will be stored there, or NULL if not
* found. If FORCED_PK is not NULL, this public key is used to verify
* _data signatures_ and no key lookup is done. Returns: 0 = valid
* signature or an error code. If R_KEYBLOCK is not NULL the keyblock
* carries the used PK is stored there. The caller should always free
* the return value using release_kbnode.
*/
static int
do_check_sig (CTX c, kbnode_t node, const void *extrahash, size_t extrahashlen,
PKT_public_key *forced_pk, int *is_selfsig,
int *is_expkey, int *is_revkey,
PKT_public_key **r_pk, kbnode_t *r_keyblock)
{
PKT_signature *sig;
gcry_md_hd_t md = NULL;
gcry_md_hd_t md2 = NULL;
gcry_md_hd_t md_good = NULL;
int algo, rc;
if (r_pk)
*r_pk = NULL;
if (r_keyblock)
*r_keyblock = NULL;
log_assert (node->pkt->pkttype == PKT_SIGNATURE);
if (is_selfsig)
*is_selfsig = 0;
sig = node->pkt->pkt.signature;
algo = sig->digest_algo;
rc = openpgp_md_test_algo (algo);
if (rc)
return rc;
if (sig->sig_class == 0x00)
{
if (c->mfx.md)
{
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
}
else /* detached signature */
{
/* check_signature() will enable the md. */
if (gcry_md_open (&md, 0, 0 ))
BUG ();
}
}
else if (sig->sig_class == 0x01)
{
/* How do we know that we have to hash the (already hashed) text
in canonical mode ??? (calculating both modes???) */
if (c->mfx.md)
{
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
if (c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2))
BUG ();
}
else /* detached signature */
{
log_debug ("Do we really need this here?");
/* check_signature() will enable the md*/
if (gcry_md_open (&md, 0, 0 ))
BUG ();
if (gcry_md_open (&md2, 0, 0 ))
BUG ();
}
}
else if ((sig->sig_class&~3) == 0x10
|| sig->sig_class == 0x18
|| sig->sig_class == 0x1f
|| sig->sig_class == 0x20
|| sig->sig_class == 0x28
|| sig->sig_class == 0x30)
{
if (c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
return check_key_signature (c->ctrl, c->list, node, is_selfsig);
}
else if (sig->sig_class == 0x20)
{
log_error (_("standalone revocation - "
"use \"gpg --import\" to apply\n"));
return GPG_ERR_NOT_PROCESSED;
}
else
{
log_error ("invalid root packet for sigclass %02x\n", sig->sig_class);
return GPG_ERR_SIG_CLASS;
}
}
else
return GPG_ERR_SIG_CLASS;
/* We only get here if we are checking the signature of a binary
(0x00) or text document (0x01). */
rc = check_signature (c->ctrl, sig, md, extrahash, extrahashlen,
forced_pk, NULL, is_expkey, is_revkey,
r_pk, r_keyblock);
if (! rc)
md_good = md;
else if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2)
{
PKT_public_key *pk2;
if (r_keyblock)
release_kbnode (*r_keyblock);
rc = check_signature (c->ctrl, sig, md2, extrahash, extrahashlen,
forced_pk, NULL, is_expkey, is_revkey,
r_pk? &pk2 : NULL, r_keyblock);
if (!rc)
{
md_good = md2;
if (r_pk)
{
free_public_key (*r_pk);
*r_pk = pk2;
}
}
}
if (md_good)
{
unsigned char *buffer = gcry_md_read (md_good, sig->digest_algo);
sig->digest_len = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (algo));
memcpy (sig->digest, buffer, sig->digest_len);
}
gcry_md_close (md);
gcry_md_close (md2);
return rc;
}
static void
print_userid (PACKET *pkt)
{
if (!pkt)
BUG();
if (pkt->pkttype != PKT_USER_ID)
{
es_printf ("ERROR: unexpected packet type %d", pkt->pkttype );
return;
}
if (opt.with_colons)
{
if (pkt->pkt.user_id->attrib_data)
es_printf("%u %lu",
pkt->pkt.user_id->numattribs,
pkt->pkt.user_id->attrib_len);
else
es_write_sanitized (es_stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len, ":", NULL);
}
else
print_utf8_buffer (es_stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len );
}
/*
* List the keyblock in a user friendly way
*/
static void
list_node (CTX c, kbnode_t node)
{
if (!node)
;
else if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
PKT_public_key *pk = node->pkt->pkt.public_key;
if (opt.with_colons)
{
u32 keyid[2];
keyid_from_pk( pk, keyid );
if (pk->flags.primary)
c->trustletter = (opt.fast_list_mode
? 0
: get_validity_info
(c->ctrl,
node->pkt->pkttype == PKT_PUBLIC_KEY
? node : NULL,
pk, NULL));
es_printf ("%s:", pk->flags.primary? "pub":"sub" );
if (c->trustletter)
es_putc (c->trustletter, es_stdout);
es_printf (":%u:%d:%08lX%08lX:%s:%s::",
nbits_from_pk( pk ),
pk->pubkey_algo,
(ulong)keyid[0],(ulong)keyid[1],
colon_datestr_from_pk( pk ),
colon_strtime (pk->expiredate) );
if (pk->flags.primary && !opt.fast_list_mode)
es_putc (get_ownertrust_info (c->ctrl, pk, 1), es_stdout);
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
else
{
print_key_line (c->ctrl, es_stdout, pk, 0);
}
if (opt.keyid_format == KF_NONE && !opt.with_colons)
; /* Already printed. */
else if ((pk->flags.primary && opt.fingerprint) || opt.fingerprint > 1)
print_fingerprint (c->ctrl, NULL, pk, 0);
if (pk->flags.primary)
{
int kl = opt.keyid_format == KF_NONE? 0 : keystrlen ();
/* Now list all userids with their signatures. */
for (node = node->next; node; node = node->next)
{
if (node->pkt->pkttype == PKT_SIGNATURE)
{
list_node (c, node );
}
else if (node->pkt->pkttype == PKT_USER_ID)
{
if (opt.with_colons)
es_printf ("%s:::::::::",
node->pkt->pkt.user_id->attrib_data?"uat":"uid");
else
es_printf ("uid%*s",
kl + (opt.legacy_list_mode? 9:11),
"" );
print_userid (node->pkt);
if (opt.with_colons)
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
list_node(c, node );
}
}
}
}
else if (node->pkt->pkttype == PKT_SECRET_KEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
log_debug ("FIXME: No way to print secret key packets here\n");
/* fixme: We may use a function to turn a secret key packet into
a public key one and use that here. */
}
else if (node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int is_selfsig = 0;
int rc2 = 0;
size_t n;
char *p;
int sigrc = ' ';
if (!opt.verbose)
return;
if (sig->sig_class == 0x20 || sig->sig_class == 0x30)
es_fputs ("rev", es_stdout);
else
es_fputs ("sig", es_stdout);
if (opt.check_sigs)
{
fflush (stdout);
rc2 = do_check_sig (c, node, NULL, 0, NULL,
&is_selfsig, NULL, NULL, NULL, NULL);
switch (gpg_err_code (rc2))
{
case 0: sigrc = '!'; break;
case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break;
default: sigrc = '%'; break;
}
}
else /* Check whether this is a self signature. */
{
u32 keyid[2];
if (c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_SECRET_KEY )
{
keyid_from_pk (c->list->pkt->pkt.public_key, keyid);
if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1])
is_selfsig = 1;
}
}
if (opt.with_colons)
{
es_putc (':', es_stdout);
if (sigrc != ' ')
es_putc (sigrc, es_stdout);
es_printf ("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
colon_datestr_from_sig (sig),
colon_expirestr_from_sig (sig));
if (sig->trust_depth || sig->trust_value)
es_printf ("%d %d",sig->trust_depth,sig->trust_value);
es_putc (':', es_stdout);
if (sig->trust_regexp)
es_write_sanitized (es_stdout, sig->trust_regexp,
strlen (sig->trust_regexp), ":", NULL);
es_putc (':', es_stdout);
}
else
es_printf ("%c %s %s ",
sigrc, keystr (sig->keyid), datestr_from_sig(sig));
if (sigrc == '%')
es_printf ("[%s] ", gpg_strerror (rc2) );
else if (sigrc == '?')
;
else if (is_selfsig)
{
if (opt.with_colons)
es_putc (':', es_stdout);
es_fputs (sig->sig_class == 0x18? "[keybind]":"[selfsig]", es_stdout);
if (opt.with_colons)
es_putc (':', es_stdout);
}
else if (!opt.fast_list_mode)
{
p = get_user_id (c->ctrl, sig->keyid, &n, NULL);
es_write_sanitized (es_stdout, p, n,
opt.with_colons?":":NULL, NULL );
xfree (p);
}
if (opt.with_colons)
es_printf (":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l');
es_putc ('\n', es_stdout);
}
else
log_error ("invalid node with packet of type %d\n", node->pkt->pkttype);
}
int
proc_packets (ctrl_t ctrl, void *anchor, iobuf_t a )
{
int rc;
CTX c = xmalloc_clear (sizeof *c);
c->ctrl = ctrl;
c->anchor = anchor;
rc = do_proc_packets (c, a, 0);
xfree (c);
return rc;
}
int
proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
strlist_t signedfiles, const char *sigfilename )
{
CTX c = xmalloc_clear (sizeof *c);
int rc;
c->ctrl = ctrl;
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = GNUPG_INVALID_FD;
c->signed_data.data_names = signedfiles;
c->signed_data.used = !!signedfiles;
c->sigfilename = sigfilename;
rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = GPG_ERR_NO_DATA;
}
/* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree (c);
return rc;
}
int
proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a,
gnupg_fd_t signed_data_fd)
{
int rc;
CTX c;
c = xtrycalloc (1, sizeof *c);
if (!c)
return gpg_error_from_syserror ();
c->ctrl = ctrl;
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = signed_data_fd;
c->signed_data.data_names = NULL;
c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD);
rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = gpg_error (GPG_ERR_NO_DATA);
}
/* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree ( c );
return rc;
}
/* Handle encryption packets. If called recursively the caller's CTX
* should be given for ANCHOR. If R_DEK and R_LIST are not NULL the
* DEK (or NULL) is returned there and the list at R_LIST; the caller
* needs to release them; even if the function returns an error. */
gpg_error_t
proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
DEK **r_dek, struct pubkey_enc_list **r_list)
{
CTX c = xmalloc_clear (sizeof *c);
int rc;
c->ctrl = ctrl;
c->anchor = anchor;
c->encrypt_only = 1;
if (r_dek && r_list)
{
rc = do_proc_packets (c, a, 1);
*r_dek = c->dek;
c->dek = NULL;
*r_list = c->pkenc_list;
c->pkenc_list = NULL;
}
else
rc = do_proc_packets (c, a, 0);
xfree (c);
return rc;
}
static int
check_nesting (CTX c)
{
int level;
for (level=0; c; c = c->anchor)
level++;
if (level > MAX_NESTING_DEPTH)
{
log_error ("input data with too deeply nested packets\n");
write_status_text (STATUS_UNEXPECTED, "1");
return GPG_ERR_BAD_DATA;
}
return 0;
}
/* Main processing loop. If KEEP_DEK_AND_LIST is set the DEK and
* PKENC_LIST of the context C are not released at the end of the
* function. The caller is then required to do this. */
static int
do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list)
{
PACKET *pkt;
struct parse_packet_ctx_s parsectx;
int rc = 0;
int any_data = 0;
int newpkt;
rc = check_nesting (c);
if (rc)
return rc;
pkt = xmalloc( sizeof *pkt );
c->iobuf = a;
init_packet(pkt);
init_parse_packet (&parsectx, a);
while ((rc=parse_packet (&parsectx, pkt)) != -1)
{
any_data = 1;
if (rc)
{
if (c->ctrl->modify_recipients && gpg_err_code (rc) == GPG_ERR_TRUE)
{
/* Save the last read CTB (which was the last byte
* actually read from the input) and get out of the
* loop. */
c->ctrl->last_read_ctb = parsectx.last_ctb;
/* We need to call the first part of the encrypted data
* handler to get the DEK. */
proc_encrypted (c, NULL);
rc = -1;
break;
}
free_packet (pkt, &parsectx);
/* Stop processing when an invalid packet has been encountered
* but don't do so when we are doing a --list-packets. */
if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
&& opt.list_packets == 0)
break;
continue;
}
newpkt = -1;
if (opt.list_packets)
{
switch (pkt->pkttype)
{
case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
default: newpkt = 0; break;
}
}
else if (c->sigs_only)
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
case PKT_SYMKEY_ENC:
case PKT_PUBKEY_ENC:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
write_status_text( STATUS_UNEXPECTED, "0" );
rc = GPG_ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break;
default: newpkt = 0; break;
}
}
else if (c->encrypt_only)
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
write_status_text (STATUS_UNEXPECTED, "0");
rc = GPG_ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_SYMKEY_ENC:
case PKT_PUBKEY_ENC:
/* In --add-recipients mode set the stop flag as soon as
* we see the first of these packets. */
if (c->ctrl->modify_recipients)
parsectx.only_fookey_enc = 1;
if (pkt->pkttype == PKT_SYMKEY_ENC)
proc_symkey_enc (c, pkt);
else
proc_pubkey_enc (c, pkt);
break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break;
default: newpkt = 0; break;
}
}
else
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
release_list (c);
c->list = new_kbnode (pkt);
newpkt = 1;
break;
case PKT_PUBLIC_SUBKEY:
case PKT_SECRET_SUBKEY:
newpkt = add_subkey (c, pkt);
break;
case PKT_USER_ID: newpkt = add_user_id (c, pkt); break;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
case PKT_RING_TRUST: newpkt = add_ring_trust (c, pkt); break;
default: newpkt = 0; break;
}
}
if (rc)
goto leave;
/* This is a very ugly construct and frankly, I don't remember why
* I used it. Adding the MDC check here is a hack.
* The right solution is to initiate another context for encrypted
* packet and not to reuse the current one ... It works right
* when there is a compression packet between which adds just
* an extra layer.
* Hmmm: Rewrite this whole module here??
*/
if (pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC)
c->any.data = (pkt->pkttype == PKT_PLAINTEXT);
if (newpkt == -1)
;
else if (newpkt)
{
pkt = xmalloc (sizeof *pkt);
init_packet (pkt);
}
else
free_packet (pkt, &parsectx);
}
if (rc == GPG_ERR_INV_PACKET)
write_status_text (STATUS_NODATA, "3");
if (any_data)
rc = 0;
else if (rc == -1)
write_status_text (STATUS_NODATA, "2");
leave:
if (!keep_dek_and_list)
release_list (c);
free_packet (pkt, &parsectx);
deinit_parse_packet (&parsectx);
xfree (pkt);
free_md_filter_context (&c->mfx);
return rc;
}
/* Return true if the AKL has the WKD method specified. */
static int
akl_has_wkd_method (void)
{
struct akl *akl;
for (akl = opt.auto_key_locate; akl; akl = akl->next)
if (akl->type == AKL_WKD)
return 1;
return 0;
}
/* Return the ISSUER fingerprint buffer and its length at R_LEN.
* Returns NULL if not available. The returned buffer is valid as
* long as SIG is not modified. */
const byte *
issuer_fpr_raw (PKT_signature *sig, size_t *r_len)
{
const byte *p;
size_t n;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_ISSUER_FPR, &n);
if (p && ((n == 21 && p[0] == 4) || (n == 33 && p[0] == 5)))
{
*r_len = n - 1;
return p+1;
}
*r_len = 0;
return NULL;
}
/* Return the ISSUER fingerprint string in human readable format if
* available. Caller must release the string. */
/* FIXME: Move to another file. */
char *
issuer_fpr_string (PKT_signature *sig)
{
const byte *p;
size_t n;
p = issuer_fpr_raw (sig, &n);
return p? bin2hex (p, n, NULL) : NULL;
}
static void
print_good_bad_signature (int statno, const char *keyid_str, kbnode_t un,
PKT_signature *sig, int rc)
{
char *p;
write_status_text_and_buffer (statno, keyid_str,
un? un->pkt->pkt.user_id->name:"[?]",
un? un->pkt->pkt.user_id->len:3,
-1);
if (un)
p = utf8_to_native (un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len, 0);
else
p = xstrdup ("[?]");
if (rc)
log_info (_("BAD signature from \"%s\""), p);
else if (sig->flags.expired)
log_info (_("Expired signature from \"%s\""), p);
else
log_info (_("Good signature from \"%s\""), p);
xfree (p);
}
static int
check_sig_and_print (CTX c, kbnode_t node)
{
PKT_signature *sig = node->pkt->pkt.signature;
const char *astr;
gpg_error_t rc;
int is_expkey = 0;
int is_revkey = 0;
char *issuer_fpr = NULL;
PKT_public_key *pk = NULL; /* The public key for the signature or NULL. */
const void *extrahash = NULL;
size_t extrahashlen = 0;
kbnode_t keyblock = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE] = { 0 };
if (opt.skip_verify)
{
log_info(_("signature verification suppressed\n"));
return 0;
}
/* Check that the message composition is valid.
*
* Per RFC-2440bis (-15) allowed:
*
* S{1,n} -- detached signature.
* S{1,n} P -- old style PGP2 signature
* O{1,n} P S{1,n} -- standard OpenPGP signature.
* C P S{1,n} -- cleartext signature.
*
*
* O = One-Pass Signature packet.
* S = Signature packet.
* P = OpenPGP Message packet (Encrypted | Compressed | Literal)
* (Note that the current rfc2440bis draft also allows
* for a signed message but that does not work as it
* introduces ambiguities.)
* We keep track of these packages using the marker packet
* CTRLPKT_PLAINTEXT_MARK.
* C = Marker packet for cleartext signatures.
*
* We reject all other messages.
*
* Actually we are calling this too often, i.e. for verification of
* each message but better have some duplicate work than to silently
* introduce a bug here.
*/
{
kbnode_t n;
int n_onepass, n_sig;
/* log_debug ("checking signature packet composition\n"); */
/* dump_kbnode (c->list); */
n = c->list;
log_assert (n);
if ( n->pkt->pkttype == PKT_SIGNATURE )
{
/* This is either "S{1,n}" case (detached signature) or
"S{1,n} P" (old style PGP2 signature). */
for (n = n->next; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (!n)
; /* Okay, this is a detached signature. */
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
{
if (n->next)
goto ambiguous; /* We only allow one P packet. */
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
}
else
goto ambiguous;
}
else if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* This is the "O{1,n} P S{1,n}" case (standard signature). */
for (n_onepass=1, n = n->next;
n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next)
n_onepass++;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (!n_sig)
goto ambiguous;
/* If we wanted to disallow multiple sig verification, we'd do
* something like this:
*
* if (n)
* goto ambiguous;
*
* However, this can stay allowable as we can't get here. */
if (n_onepass != n_sig)
{
log_info ("number of one-pass packets does not match "
"number of signature packets\n");
goto ambiguous;
}
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
{
/* This is the "C P S{1,n}" case (clear text signature). */
n = n->next;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (n || !n_sig)
goto ambiguous;
}
else
{
ambiguous:
log_error(_("can't handle this ambiguous signature data\n"));
rc = 0;
goto leave;
}
} /* End checking signature packet composition. */
if (sig->signers_uid)
write_status_buffer (STATUS_NEWSIG,
sig->signers_uid, strlen (sig->signers_uid), 0);
else
write_status_text (STATUS_NEWSIG, NULL);
astr = openpgp_pk_algo_name ( sig->pubkey_algo );
issuer_fpr = issuer_fpr_string (sig);
if (issuer_fpr)
{
log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp));
log_info (_(" using %s key %s\n"),
astr? astr: "?", issuer_fpr);
}
else if (!keystrlen () || keystrlen () > 8)
{
log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp));
log_info (_(" using %s key %s\n"),
astr? astr: "?", keystr(sig->keyid));
}
else /* Legacy format. */
log_info (_("Signature made %s using %s key ID %s\n"),
asctimestamp(sig->timestamp), astr? astr: "?",
keystr(sig->keyid));
/* In verbose mode print the signers UID. */
if (sig->signers_uid)
log_info (_(" issuer \"%s\"\n"), sig->signers_uid);
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk, &keyblock);
/* If the key is not found but the signature includes a key block we
* use that key block for verification and on success import it. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& sig->flags.key_block
&& opt.flags.auto_key_import)
{
kbnode_t included_keyblock = NULL;
PKT_public_key *included_pk;
const byte *kblock;
size_t kblock_len;
included_pk = xcalloc (1, sizeof *included_pk);
kblock = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_BLOCK, &kblock_len);
if (kblock && kblock_len > 1
&& !get_pubkey_from_buffer (c->ctrl, included_pk,
kblock+1, kblock_len-1,
sig->keyid, &included_keyblock))
{
/* Note: This is the only place where we use the forced_pk
* arg (ie. included_pk) with do_check_sig. */
rc = do_check_sig (c, node, extrahash, extrahashlen, included_pk,
NULL, &is_expkey, &is_revkey, &pk, NULL);
if (opt.verbose)
log_info ("checked signature using included key block: %s\n",
gpg_strerror (rc));
if (!rc)
{
/* The keyblock has been verified, we now import it. */
rc = import_included_key_block (c->ctrl, included_keyblock);
}
}
free_public_key (included_pk);
release_kbnode (included_keyblock);
/* To make sure that nothing strange happened we check the
* signature again now using our own key store. This also
* returns the keyblock which we use later on. */
if (!rc)
{
release_kbnode (keyblock);
keyblock = NULL;
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk, &keyblock);
}
}
/* If the key isn't found, check for a preferred keyserver. Note
* that this is only done if honor-keyserver-url has been set. We
* test for this in the loop so that we can show info about the
* preferred keyservers. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& sig->flags.pref_ks)
{
const byte *p;
int seq = 0;
size_t n;
int any_pref_ks = 0;
while ((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &n, &seq, NULL)))
{
/* According to my favorite copy editor, in English grammar,
you say "at" if the key is located on a web page, but
"from" if it is located on a keyserver. I'm not going to
even try to make two strings here :) */
log_info(_("Key available at: ") );
print_utf8_buffer (log_get_stream(), p, n);
log_printf ("\n");
any_pref_ks = 1;
if ((opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& (opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL))
{
struct keyserver_spec *spec;
spec = parse_preferred_keyserver (sig);
if (spec)
{
int res;
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n",
"Pref-KS");
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_keyid (c->ctrl, sig->keyid,spec,
KEYSERVER_IMPORT_FLAG_QUICK);
glo_ctrl.in_auto_key_retrieve--;
if (!res)
{
release_kbnode (keyblock);
keyblock = NULL;
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk,
&keyblock);
}
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "Pref-KS",
gpg_strerror (res));
free_keyserver_spec (spec);
if (!rc)
break;
}
}
}
if (any_pref_ks
&& (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& !(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL))
log_info (_("Note: Use '%s' to make use of this info\n"),
"--keyserver-option honor-keyserver-url");
}
/* If the above methods didn't work, our next try is to retrieve the
* key from the WKD. This requires that WKD is in the AKL and the
* Signer's UID is in the signature. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE)
&& !opt.flags.disable_signer_uid
&& akl_has_wkd_method ()
&& sig->signers_uid)
{
int res;
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n", "WKD");
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_wkd (c->ctrl, sig->signers_uid,
KEYSERVER_IMPORT_FLAG_QUICK, NULL, NULL);
glo_ctrl.in_auto_key_retrieve--;
/* Fixme: If the fingerprint is embedded in the signature,
* compare it to the fingerprint of the returned key. */
if (!res)
{
release_kbnode (keyblock);
keyblock = NULL;
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk, &keyblock);
}
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "WKD", gpg_strerror (res));
}
/* If the above methods didn't work, our next try is to locate
* the key via its fingerprint from a keyserver. This requires
* that the signers fingerprint is encoded in the signature. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& keyserver_any_configured (c->ctrl))
{
int res;
const byte *p;
size_t n;
p = issuer_fpr_raw (sig, &n);
if (p)
{
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n", "KS");
/* v4 or v5 packet with a SHA-1/256 fingerprint. */
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_fpr (c->ctrl, p, n, opt.keyserver,
KEYSERVER_IMPORT_FLAG_QUICK);
glo_ctrl.in_auto_key_retrieve--;
if (!res)
{
release_kbnode (keyblock);
keyblock = NULL;
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk,
&keyblock);
}
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "KS", gpg_strerror (res));
}
}
/* Do something with the result of the signature checking. */
if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE)
{
/* We have checked the signature and the result is either a good
* signature or a bad signature. Further examination follows. */
kbnode_t un;
int count = 0;
int keyblock_has_pk = 0; /* For failsafe check. */
int statno;
char keyid_str[50];
PKT_public_key *mainpk = NULL;
if (rc)
statno = STATUS_BADSIG;
else if (sig->flags.expired)
statno = STATUS_EXPSIG;
else if (is_expkey)
statno = STATUS_EXPKEYSIG;
else if(is_revkey)
statno = STATUS_REVKEYSIG;
else
statno = STATUS_GOODSIG;
snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
/* Find and print the primary user ID along with the
"Good|Expired|Bad signature" line. */
for (un=keyblock; un; un = un->next)
{
int valid;
if (!keyblock_has_pk
&& (un->pkt->pkttype == PKT_PUBLIC_KEY
|| un->pkt->pkttype == PKT_PUBLIC_SUBKEY)
&& !cmp_public_keys (un->pkt->pkt.public_key, pk))
{
keyblock_has_pk = 1;
}
if (un->pkt->pkttype == PKT_PUBLIC_KEY)
{
mainpk = un->pkt->pkt.public_key;
continue;
}
if (un->pkt->pkttype != PKT_USER_ID)
continue;
if (!un->pkt->pkt.user_id->created)
continue;
if (un->pkt->pkt.user_id->flags.revoked)
continue;
if (un->pkt->pkt.user_id->flags.expired)
continue;
if (!un->pkt->pkt.user_id->flags.primary)
continue;
/* We want the textual primary user ID here */
if (un->pkt->pkt.user_id->attrib_data)
continue;
log_assert (mainpk);
/* Since this is just informational, don't actually ask the
user to update any trust information. (Note: we register
the signature later.) Because print_good_bad_signature
does not print a LF we need to compute the validity
before calling that function. */
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
valid = get_validity (c->ctrl, keyblock, mainpk,
un->pkt->pkt.user_id, NULL, 0);
else
valid = 0; /* Not used. */
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
print_good_bad_signature (statno, keyid_str, un, sig, rc);
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
log_printf (" [%s]\n",trust_value_to_string(valid));
else
log_printf ("\n");
count++;
/* At this point we could in theory stop because the primary
* UID flag is never set for more than one User ID per
* keyblock. However, we use this loop also for a failsafe
* check that the public key used to create the signature is
* contained in the keyring.*/
}
if (!mainpk || !keyblock_has_pk)
{
log_error ("signature key lost from keyblock (%p,%p,%d)\n",
keyblock, mainpk, keyblock_has_pk);
rc = gpg_error (GPG_ERR_INTERNAL);
}
/* In case we did not found a valid textual userid above
we print the first user id packet or a "[?]" instead along
with the "Good|Expired|Bad signature" line. */
if (!count)
{
/* Try for an invalid textual userid */
for (un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype == PKT_USER_ID
&& !un->pkt->pkt.user_id->attrib_data)
break;
}
/* Try for any userid at all */
if (!un)
{
for (un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype == PKT_USER_ID)
break;
}
}
if (opt.trust_model==TM_ALWAYS || !un)
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
print_good_bad_signature (statno, keyid_str, un, sig, rc);
if (opt.trust_model != TM_ALWAYS && un)
log_printf (" %s",_("[uncertain]") );
log_printf ("\n");
}
/* If we have a good signature and already printed
* the primary user ID, print all the other user IDs */
if (count
&& !rc
&& !(opt.verify_options & VERIFY_SHOW_PRIMARY_UID_ONLY))
{
char *p;
for( un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype != PKT_USER_ID)
continue;
if ((un->pkt->pkt.user_id->flags.revoked
|| un->pkt->pkt.user_id->flags.expired)
&& !(opt.verify_options & VERIFY_SHOW_UNUSABLE_UIDS))
continue;
/* Skip textual primary user ids which we printed above. */
if (un->pkt->pkt.user_id->flags.primary
&& !un->pkt->pkt.user_id->attrib_data )
continue;
/* If this user id has attribute data, print that. */
if (un->pkt->pkt.user_id->attrib_data)
{
dump_attribs (un->pkt->pkt.user_id, mainpk);
if (opt.verify_options&VERIFY_SHOW_PHOTOS)
show_photos (c->ctrl,
un->pkt->pkt.user_id->attribs,
un->pkt->pkt.user_id->numattribs,
mainpk ,un->pkt->pkt.user_id);
}
p = utf8_to_native (un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len, 0);
log_info (_(" aka \"%s\""), p);
xfree (p);
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
{
const char *valid;
if (un->pkt->pkt.user_id->flags.revoked)
valid = _("revoked");
else if (un->pkt->pkt.user_id->flags.expired)
valid = _("expired");
else
/* Since this is just informational, don't
actually ask the user to update any trust
information. */
valid = (trust_value_to_string
(get_validity (c->ctrl, keyblock, mainpk,
un->pkt->pkt.user_id, NULL, 0)));
log_printf (" [%s]\n",valid);
}
else
log_printf ("\n");
}
}
/* For good signatures print notation data. */
if (!rc)
{
if ((opt.verify_options & VERIFY_SHOW_POLICY_URLS))
show_policy_url (sig, 0, 1);
else
show_policy_url (sig, 0, 2);
if ((opt.verify_options & VERIFY_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 0, 1);
else
show_keyserver_url (sig, 0, 2);
if ((opt.verify_options & VERIFY_SHOW_NOTATIONS))
show_notation
(sig, 0, 1,
(((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)
+ ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0)
+ ((opt.verify_options &VERIFY_SHOW_HIDDEN_NOTATIONS)? 4:0)
));
else
show_notation (sig, 0, 2, 0);
+ print_matching_notations (sig);
}
/* Fill PKSTRBUF with the algostring in case we later need it. */
if (pk)
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
/* For good signatures print the VALIDSIG status line. */
if (!rc && (is_status_enabled ()
|| opt.assert_signer_list
|| opt.assert_pubkey_algos) && pk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
hexfingerprint (pk, pkhex, sizeof pkhex);
hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
/* TODO: Replace the reserved '0' in the field below with
bits for status flags (policy url, notation, etc.). */
write_status_printf (STATUS_VALIDSIG,
"%s %s %lu %lu %d 0 %d %d %02X %s",
pkhex,
strtimestamp (sig->timestamp),
(ulong)sig->timestamp,
(ulong)sig->expiredate,
sig->version, sig->pubkey_algo,
sig->digest_algo,
sig->sig_class,
mainpkhex);
/* Handle the --assert-signer option. */
check_assert_signer_list (mainpkhex, pkhex);
/* Handle the --assert-pubkey-algo option. */
check_assert_pubkey_algo (pkstrbuf, pkhex);
}
/* Print compliance warning for Good signatures. */
if (!rc && pk && !opt.quiet
&& !gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
{
log_info (_("WARNING: This key is not suitable for signing"
" in %s mode\n"),
gnupg_compliance_option_string (opt.compliance));
}
/* For good signatures compute and print the trust information.
Note that in the Tofu trust model this may ask the user on
how to resolve a conflict. */
if (!rc)
{
rc = check_signatures_trust (c->ctrl, keyblock, pk, sig);
}
/* Print extra information about the signature. */
if (sig->flags.expired)
{
log_info (_("Signature expired %s\n"), asctimestamp(sig->expiredate));
if (!rc)
rc = gpg_error (GPG_ERR_GENERAL); /* Need a better error here? */
}
else if (sig->expiredate)
log_info (_("Signature expires %s\n"), asctimestamp(sig->expiredate));
if (opt.verbose)
{
log_info (_("%s signature, digest algorithm %s%s%s\n"),
sig->sig_class==0x00?_("binary"):
sig->sig_class==0x01?_("textmode"):_("unknown"),
gcry_md_algo_name (sig->digest_algo),
*pkstrbuf?_(", key algorithm "):"", pkstrbuf);
}
/* Print final warnings. */
if (!rc && !c->signed_data.used)
{
/* Signature is basically good but we test whether the
deprecated command
gpg --verify FILE.sig
was used instead of
gpg --verify FILE.sig FILE
to verify a detached signature. If we figure out that a
data file with a matching name exists, we print a warning.
The problem is that the first form would also verify a
standard signature. This behavior could be used to
create a made up .sig file for a tarball by creating a
standard signature from a valid detached signature packet
(for example from a signed git tag). Then replace the
sig file on the FTP server along with a changed tarball.
Using the first form the verify command would correctly
verify the signature but don't even consider the tarball. */
kbnode_t n;
char *dfile;
dfile = get_matching_datafile (c->sigfilename);
if (dfile)
{
for (n = c->list; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (n)
{
/* Not only signature packets in the tree thus this
is not a detached signature. */
log_info (_("WARNING: not a detached signature; "
"file '%s' was NOT verified!\n"), dfile);
assert_signer_true = 0;
}
xfree (dfile);
}
}
/* Compute compliance with CO_DE_VS. */
if (pk
&& gnupg_gcrypt_is_compliant (CO_DE_VS)
&& gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits_from_pk (pk), NULL)
&& gnupg_digest_is_compliant (CO_DE_VS, sig->digest_algo))
write_status_strings (STATUS_VERIFICATION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
else if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
if (!rc)
rc = gpg_error (GPG_ERR_FORBIDDEN);
}
free_public_key (pk);
pk = NULL;
release_kbnode( keyblock );
if (rc)
g10_errors_seen = 1;
}
else /* Error checking the signature. (neither Good nor Bad). */
{
write_status_printf (STATUS_ERRSIG, "%08lX%08lX %d %d %02x %lu %d %s",
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->pubkey_algo, sig->digest_algo,
sig->sig_class, (ulong)sig->timestamp,
gpg_err_code (rc),
issuer_fpr? issuer_fpr:"-");
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
{
write_status_printf (STATUS_NO_PUBKEY, "%08lX%08lX",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
}
if (gpg_err_code (rc) != GPG_ERR_NOT_PROCESSED)
log_error (_("Can't check signature: %s\n"), gpg_strerror (rc));
}
leave:
free_public_key (pk);
xfree (issuer_fpr);
return rc;
}
/*
* Process the tree which starts at node
*/
static void
proc_tree (CTX c, kbnode_t node)
{
kbnode_t n1;
int rc;
if (opt.list_packets || opt.list_only)
return;
/* We must skip our special plaintext marker packets here because
they may be the root packet. These packets are only used in
additional checks and skipping them here doesn't matter. */
while (node
&& node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK)
{
node = node->next;
}
if (!node)
return;
c->trustletter = ' ';
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
merge_keys_and_selfsig (c->ctrl, node);
list_node (c, node);
}
else if (node->pkt->pkttype == PKT_SECRET_KEY)
{
merge_keys_and_selfsig (c->ctrl, node);
list_node (c, node);
}
else if (node->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* Check all signatures. */
if (!c->any.data)
{
int use_textmode = 0;
free_md_filter_context (&c->mfx);
/* Prepare to create all requested message digests. */
rc = gcry_md_open (&c->mfx.md, 0, 0);
if (rc)
goto hash_err;
/* Fixme: why looking for the signature packet and not the
one-pass packet? */
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
gcry_md_enable (c->mfx.md, n1->pkt->pkt.signature->digest_algo);
if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01)
use_textmode = 1;
/* Ask for file and hash it. */
if (c->sigs_only)
{
if (c->signed_data.used
&& c->signed_data.data_fd != GNUPG_INVALID_FD)
rc = hash_datafile_by_fd (c->mfx.md, NULL,
c->signed_data.data_fd,
use_textmode);
else
rc = hash_datafiles (c->mfx.md, NULL,
c->signed_data.data_names,
c->sigfilename,
use_textmode);
}
else
{
rc = ask_for_detached_datafile (c->mfx.md, NULL,
iobuf_get_real_fname (c->iobuf),
use_textmode);
}
hash_err:
if (rc)
{
log_error ("can't hash datafile: %s\n", gpg_strerror (rc));
return;
}
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else if (node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START)
{
/* Clear text signed message. */
if (!c->any.data)
{
log_error ("cleartext signature without data\n");
return;
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else if (node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int multiple_ok = 1;
n1 = find_next_kbnode (node, PKT_SIGNATURE);
if (n1)
{
byte class = sig->sig_class;
byte hash = sig->digest_algo;
for (; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
{
/* We can't currently handle multiple signatures of
* different classes (we'd pretty much have to run a
* different hash context for each), but if they are all
* the same and it is detached signature, we make an
* exception. Note that the old code also disallowed
* multiple signatures if the digest algorithms are
* different. We softened this restriction only for
* detached signatures, to be on the safe side. */
if (n1->pkt->pkt.signature->sig_class != class
|| (c->any.data
&& n1->pkt->pkt.signature->digest_algo != hash))
{
multiple_ok = 0;
log_info (_("WARNING: multiple signatures detected. "
"Only the first will be checked.\n"));
break;
}
}
}
if (sig->sig_class != 0x00 && sig->sig_class != 0x01)
{
log_info(_("standalone signature of class 0x%02x\n"), sig->sig_class);
}
else if (!c->any.data)
{
/* Detached signature */
free_md_filter_context (&c->mfx);
rc = gcry_md_open (&c->mfx.md, sig->digest_algo, 0);
if (rc)
goto detached_hash_err;
if (multiple_ok)
{
/* If we have and want to handle multiple signatures we
* need to enable all hash algorithms for the context. */
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE)); )
if (!openpgp_md_test_algo (n1->pkt->pkt.signature->digest_algo))
gcry_md_enable (c->mfx.md,
map_md_openpgp_to_gcry
(n1->pkt->pkt.signature->digest_algo));
}
if (RFC2440 || RFC4880)
; /* Strict RFC mode. */
else if (sig->digest_algo == DIGEST_ALGO_SHA1
&& sig->pubkey_algo == PUBKEY_ALGO_DSA
&& sig->sig_class == 0x01)
{
/* Enable a workaround for a pgp5 bug when the detached
* signature has been created in textmode. Note that we
* do not implement this for multiple signatures with
* different hash algorithms. */
rc = gcry_md_open (&c->mfx.md2, sig->digest_algo, 0);
if (rc)
goto detached_hash_err;
}
/* Here we used to have another hack to work around a pgp
* 2 bug: It worked by not using the textmode for detached
* signatures; this would let the first signature check
* (on md) fail but the second one (on md2), which adds an
* extra CR would then have produced the "correct" hash.
* This is very, very ugly hack but it may haved help in
* some cases (and break others).
* c->mfx.md2? 0 :(sig->sig_class == 0x01)
*/
if (DBG_HASHING)
{
gcry_md_debug (c->mfx.md, "verify");
if (c->mfx.md2)
gcry_md_debug (c->mfx.md2, "verify2");
}
if (c->sigs_only)
{
if (c->signed_data.used
&& c->signed_data.data_fd != GNUPG_INVALID_FD)
rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2,
c->signed_data.data_fd,
(sig->sig_class == 0x01));
else
rc = hash_datafiles (c->mfx.md, c->mfx.md2,
c->signed_data.data_names,
c->sigfilename,
(sig->sig_class == 0x01));
}
else
{
rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2,
iobuf_get_real_fname(c->iobuf),
(sig->sig_class == 0x01));
}
detached_hash_err:
if (rc)
{
log_error ("can't hash datafile: %s\n", gpg_strerror (rc));
return;
}
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
else if (!opt.quiet)
log_info (_("old style (PGP 2.x) signature\n"));
if (multiple_ok)
{
for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else
check_sig_and_print (c, node);
}
else
{
dump_kbnode (c->list);
log_error ("invalid root packet detected in proc_tree()\n");
dump_kbnode (node);
}
}
diff --git a/g10/options.h b/g10/options.h
index cd5c19f45..28fc2207a 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -1,490 +1,491 @@
/* options.h
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2010, 2011 Free Software Foundation, Inc.
* Copyright (C) 2015 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#ifndef G10_OPTIONS_H
#define G10_OPTIONS_H
#include <sys/types.h>
#include "../common/types.h"
#include <stdint.h>
#include "main.h"
#include "packet.h"
#include "tofu.h"
#include "../common/session-env.h"
#include "../common/compliance.h"
/* Object to hold information pertaining to a keyserver; it also
allows building a list of keyservers. For historic reasons this is
not a strlist_t. */
struct keyserver_spec
{
struct keyserver_spec *next;
char *uri;
};
typedef struct keyserver_spec *keyserver_spec_t;
/* The --auto-key-locate mechanisms object. */
struct akl
{
enum {
AKL_NODEFAULT,
AKL_LOCAL,
AKL_CERT,
AKL_PKA,
AKL_DANE,
AKL_WKD,
AKL_LDAP,
AKL_NTDS,
AKL_KEYSERVER,
AKL_SPEC
} type;
keyserver_spec_t spec;
struct akl *next;
};
/* Global options for GPG. */
EXTERN_UNLESS_MAIN_MODULE
struct
{
int verbose;
int quiet;
unsigned debug;
int armor;
char *outfile;
estream_t outfp; /* Hack, sometimes used in place of outfile. */
off_t max_output;
/* If > 0 a hint with the expected number of input data bytes. This
* is not necessary an exact number but intended to be used for
* progress info and to decide on how to allocate buffers. */
uint64_t input_size_hint;
/* The AEAD chunk size expressed as a power of 2. */
int chunk_size;
int dry_run;
int autostart;
int list_only;
int mimemode;
int textmode;
int expert;
const char *def_sig_expire;
int ask_sig_expire;
const char *def_cert_expire;
int ask_cert_expire;
int batch; /* run in batch mode */
int answer_yes; /* answer yes on most questions */
int answer_no; /* answer no on most questions */
int check_sigs; /* check key signatures */
int with_colons;
int with_key_data;
int with_icao_spelling; /* Print ICAO spelling with fingerprints. */
int with_v5_fingerprint; /* Option --with-v5-fingerprint active. */
int with_fingerprint; /* Option --with-fingerprint active. */
int with_subkey_fingerprint; /* Option --with-subkey-fingerprint active. */
int with_keygrip; /* Option --with-keygrip active. */
int with_key_screening;/* Option --with-key-screening active. */
int with_tofu_info; /* Option --with-tofu_info active. */
int with_secret; /* Option --with-secret active. */
int with_wkd_hash; /* Option --with-wkd-hash. */
int with_key_origin; /* Option --with-key-origin. */
int fingerprint; /* list fingerprints */
int list_sigs; /* list signatures */
int no_armor;
int list_packets; /* Option --list-packets active. */
int def_cipher_algo;
int force_mdc;
int disable_mdc;
int force_aead;
int def_digest_algo;
int cert_digest_algo;
int compress_algo;
int explicit_compress_option; /* A compress option was explicitly given. */
int compress_level;
int bz2_compress_level;
int bz2_decompress_lowmem;
strlist_t def_secret_key;
char *def_recipient;
int def_recipient_self;
strlist_t secret_keys_to_try;
/* A list of mail addresses (addr-spec) provided by the user with
* the option --sender. */
strlist_t sender_list;
/* A list of fingerprints added as designated revokers to new keys. */
strlist_t desig_revokers;
int def_cert_level;
int min_cert_level;
int ask_cert_level;
int emit_version; /* 0 = none,
1 = major only,
2 = major and minor,
3 = full version,
4 = full version plus OS string. */
int marginals_needed;
int completes_needed;
int max_cert_depth;
char *agent_program;
char *keyboxd_program;
char *dirmngr_program;
int disable_dirmngr;
const char *def_new_key_algo;
strlist_t def_new_key_adsks; /* Option --default-new-key-adsk. */
/* Options to be passed to the gpg-agent */
session_env_t session_env;
char *lc_ctype;
char *lc_messages;
int skip_verify;
int skip_hidden_recipients;
/* TM_CLASSIC must be zero to accommodate trustdbsg generated before
we started storing the trust model inside the trustdb. */
enum
{
TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2,
TM_ALWAYS, TM_DIRECT, TM_AUTO, TM_TOFU, TM_TOFU_PGP
} trust_model;
enum tofu_policy tofu_default_policy;
int force_ownertrust;
enum gnupg_compliance_mode compliance;
enum
{
KF_DEFAULT, KF_NONE, KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG
} keyid_format;
const char *set_filename;
strlist_t comments;
int throw_keyids;
const char *photo_viewer;
int s2k_mode;
int s2k_digest_algo;
int s2k_cipher_algo;
unsigned char s2k_count; /* This is the encoded form, not the raw
count */
int not_dash_escaped;
int escape_from;
int lock_once;
keyserver_spec_t keyserver; /* The list of configured keyservers. */
struct
{
unsigned int options;
unsigned int import_options;
unsigned int export_options;
char *http_proxy;
} keyserver_options;
int exec_disable;
int exec_path_set;
unsigned int import_options;
unsigned int export_options;
unsigned int list_options;
unsigned int verify_options;
const char *def_preference_list;
const char *def_keyserver_url;
prefitem_t *personal_cipher_prefs;
prefitem_t *personal_digest_prefs;
prefitem_t *personal_compress_prefs;
struct weakhash *weak_digests;
int no_perm_warn;
char *temp_dir;
int no_encrypt_to;
int encrypt_to_default_key;
int interactive;
+ strlist_t print_notations; /* Name of notations to print. */
struct notation *sig_notations;
struct notation *cert_notations;
strlist_t sig_policy_url;
strlist_t cert_policy_url;
strlist_t sig_keyserver_url;
strlist_t cert_subpackets;
strlist_t sig_subpackets;
int allow_non_selfsigned_uid;
int allow_freeform_uid;
int no_literal;
ulong set_filesize;
int fast_list_mode;
int legacy_list_mode;
int ignore_time_conflict;
int ignore_valid_from;
int ignore_crc_error;
int ignore_mdc_error;
int ignore_expiration;
int command_fd;
const char *override_session_key;
unsigned int show_session_key:1;
unsigned int show_only_session_key:1;
const char *gpg_agent_info;
int try_all_secrets;
int no_expensive_trust_checks;
int no_sig_cache;
int no_auto_check_trustdb;
int preserve_permissions;
int no_homedir_creation;
struct groupitem *grouplist;
int mangle_dos_filenames;
int enable_progress_filter;
unsigned int screen_columns;
unsigned int screen_lines;
byte *show_subpackets;
int rfc2440_text;
unsigned int min_rsa_length; /* Used for compliance checks. */
/* If true, let write failures on the status-fd exit the process. */
int exit_on_status_write_error;
/* If > 0, limit the number of card insertion prompts to this
value. */
int limit_card_insert_tries;
/* The list of --assert-signer option values. Note: The values are
* modify to be uppercase if they represent a fingerrint */
strlist_t assert_signer_list;
/* A single string with the comma delimited args from
* --assert-pubkey_algo. */
char *assert_pubkey_algos;
struct
{
/* If set, require an 0x19 backsig to be present on signatures
made by signing subkeys. If not set, a missing backsig is not
an error (but an invalid backsig still is). */
unsigned int require_cross_cert:1;
unsigned int use_embedded_filename:1;
unsigned int utf8_filename:1;
unsigned int dsa2:1;
unsigned int allow_old_cipher_algos:1;
unsigned int allow_weak_digest_algos:1;
unsigned int allow_weak_key_signatures:1;
unsigned int large_rsa:1;
unsigned int disable_signer_uid:1;
unsigned int include_key_block:1;
unsigned int auto_key_import:1;
/* Flag to enable experimental features from RFC4880bis. */
unsigned int rfc4880bis:1;
/* Hack: --output is not given but OUTFILE was temporary set to "-". */
unsigned int dummy_outfile:1;
/* Force the use of the OpenPGP card and do not allow the use of
* another card. */
unsigned int use_only_openpgp_card:1;
unsigned int full_timestrings:1;
/* Force signing keys even if a key signature already exists. */
unsigned int force_sign_key:1;
/* On key generation do not set the ownertrust. */
unsigned int no_auto_trust_new_key:1;
/* The next flag is set internally iff IMPORT_SELF_SIGS_ONLY has
* been set by the user and is not the default value. */
unsigned int expl_import_self_sigs_only:1;
/* The next flag is set internally iff IMPORT_CLEAN has
* been set by the user and is not the default value. */
unsigned int expl_import_clean:1;
/* Fail if an operation can't be done in the requested compliance
* mode. */
unsigned int require_compliance:1;
/* Fail encryption unless a PQC algorithm is used. */
unsigned int require_pqc_encryption:1;
/* Do not use PQC subkeys for encryption. This is never set if
* require_pqc_encryption is also set. */
unsigned int disable_pqc_encryption:1;
/* Process all signatures even in batch mode. */
unsigned int proc_all_sigs:1;
} flags;
/* Linked list of ways to find a key if the key isn't on the local
keyring. */
struct akl *auto_key_locate;
/* The value of --key-origin. See parse_key_origin(). */
int key_origin;
char *key_origin_url;
int passphrase_repeat;
int pinentry_mode;
int request_origin;
int unwrap_encryption;
int only_sign_text_ids;
int no_symkey_cache; /* Disable the cache used for --symmetric. */
int use_keyboxd; /* Use the external keyboxd as storage backend. */
/* Compatibility flags (COMPAT_FLAG_xxxx). */
unsigned int compat_flags;
} opt;
/* CTRL is used to keep some global variables we currently can't
avoid. Future concurrent versions of gpg will put it into a per
request structure CTRL. */
EXTERN_UNLESS_MAIN_MODULE
struct {
int in_auto_key_retrieve; /* True if we are doing an
auto_key_retrieve. */
/* Hack to store the last error. We currently need it because the
proc_packet machinery is not able to reliabale return error
codes. Thus for the --server purposes we store some of the error
codes here. FIXME! */
gpg_error_t lasterr;
/* Kludge to silence some warnings using --secret-key-list. */
int silence_parse_warnings;
} glo_ctrl;
#define DBG_PACKET_VALUE 1 /* debug packet reading/writing */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug crypto handling */
/* (may reveal sensitive data) */
#define DBG_FILTER_VALUE 8 /* debug internal filter handling */
#define DBG_IOBUF_VALUE 16 /* debug iobuf stuff */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_TRUST_VALUE 256 /* debug the trustdb */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_IPC_VALUE 1024 /* debug assuan communication */
#define DBG_RECSEL_VALUE 2048 /* Debug the record selection */
#define DBG_CLOCK_VALUE 4096
#define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */
#define DBG_EXTPROG_VALUE 16384 /* debug external program calls */
#define DBG_KEYDB_VALUE 32768 /* debug keydb and keyboxd searches. */
/* Tests for the debugging flags. */
#define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
#define DBG_MPI (opt.debug & DBG_MPI_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_FILTER (opt.debug & DBG_FILTER_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
#define DBG_RECSEL (opt.debug & DBG_RECSEL_VALUE)
#define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE)
#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE)
#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
#define DBG_KEYDB (opt.debug & DBG_KEYDB_VALUE)
/* FIXME: We need to check why we did not put this into opt. */
#define DBG_MEMORY memory_debug_mode
#define DBG_MEMSTAT memory_stat_debug_mode
EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode;
EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
/* Compatibility flags */
#define COMPAT_PARALLELIZED 1 /* Use threaded hashing for signatures. */
#define COMPAT_T7014_OLD 2 /* Use initial T7014 test data. */
#define COMPAT_COMPR_KEYS 4 /* Allow import of compressed keys. (T7014) */
#define COMPAT_NO_MANU 8 /* Do not include a "manu" notation. */
/* Compliance test macros. */
#define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS)
#define RFC2440 (opt.compliance==CO_RFC2440)
#define RFC4880 (opt.compliance==CO_RFC4880)
#define PGP7 (opt.compliance==CO_PGP7)
#define PGP8 (opt.compliance==CO_PGP8)
#define PGPX (PGP7 || PGP8)
/* Various option flags. Note that there should be no common string
names between the IMPORT_ and EXPORT_ flags as they can be mixed in
the keyserver-options option. */
#define IMPORT_LOCAL_SIGS (1<<0)
#define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1)
#define IMPORT_FAST (1<<2)
#define IMPORT_SHOW (1<<3)
#define IMPORT_MERGE_ONLY (1<<4)
#define IMPORT_MINIMAL (1<<5)
#define IMPORT_CLEAN (1<<6)
#define IMPORT_ONLY_PUBKEYS (1<<7)
#define IMPORT_KEEP_OWNERTTRUST (1<<8)
#define IMPORT_EXPORT (1<<9)
#define IMPORT_RESTORE (1<<10)
#define IMPORT_REPAIR_KEYS (1<<11)
#define IMPORT_DRY_RUN (1<<12)
#define IMPORT_SELF_SIGS_ONLY (1<<14)
#define IMPORT_COLLAPSE_UIDS (1<<15)
#define IMPORT_COLLAPSE_SUBKEYS (1<<16)
#define IMPORT_BULK (1<<17)
#define IMPORT_IGNORE_ATTRIBUTES (1<<18)
#define EXPORT_LOCAL_SIGS (1<<0)
#define EXPORT_ATTRIBUTES (1<<1)
#define EXPORT_SENSITIVE_REVKEYS (1<<2)
#define EXPORT_RESET_SUBKEY_PASSWD (1<<3)
#define EXPORT_MINIMAL (1<<5)
#define EXPORT_CLEAN (1<<6)
#define EXPORT_DANE_FORMAT (1<<7)
#define EXPORT_BACKUP (1<<10)
#define EXPORT_REVOCS (1<<11)
#define EXPORT_MODE1003 (1<<12)
#define EXPORT_REALCLEAN (1<<13)
#define LIST_SHOW_PHOTOS (1<<0)
#define LIST_SHOW_POLICY_URLS (1<<1)
#define LIST_SHOW_STD_NOTATIONS (1<<2)
#define LIST_SHOW_USER_NOTATIONS (1<<3)
#define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS)
#define LIST_SHOW_KEYSERVER_URLS (1<<4)
#define LIST_SHOW_UID_VALIDITY (1<<5)
#define LIST_SHOW_UNUSABLE_UIDS (1<<6)
#define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7)
#define LIST_SHOW_KEYRING (1<<8)
#define LIST_SHOW_SIG_EXPIRE (1<<9)
#define LIST_SHOW_SIG_SUBPACKETS (1<<10)
#define LIST_SHOW_USAGE (1<<11)
#define LIST_SHOW_ONLY_FPR_MBOX (1<<12)
#define LIST_SORT_SIGS (1<<13)
#define LIST_SHOW_PREF (1<<14)
#define LIST_SHOW_PREF_VERBOSE (1<<15)
#define LIST_SHOW_UNUSABLE_SIGS (1<<16)
#define LIST_SHOW_X509_NOTATIONS (1<<17)
#define LIST_STORE_X509_NOTATIONS (1<<18)
#define LIST_SHOW_OWNERTRUST (1<<19)
#define LIST_SHOW_TRUSTSIG (1<<20)
#define LIST_SHOW_HIDDEN_NOTATIONS (1<<21)
#define VERIFY_SHOW_PHOTOS (1<<0)
#define VERIFY_SHOW_POLICY_URLS (1<<1)
#define VERIFY_SHOW_STD_NOTATIONS (1<<2)
#define VERIFY_SHOW_USER_NOTATIONS (1<<3)
#define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS)
#define VERIFY_SHOW_KEYSERVER_URLS (1<<4)
#define VERIFY_SHOW_UID_VALIDITY (1<<5)
#define VERIFY_SHOW_UNUSABLE_UIDS (1<<6)
#define VERIFY_SHOW_PRIMARY_UID_ONLY (1<<9)
#define VERIFY_SHOW_HIDDEN_NOTATIONS (1<<21)
#define KEYSERVER_HTTP_PROXY (1<<0)
#define KEYSERVER_TIMEOUT (1<<1)
#define KEYSERVER_ADD_FAKE_V3 (1<<2)
#define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3)
#define KEYSERVER_HONOR_KEYSERVER_URL (1<<4)
#endif /*G10_OPTIONS_H*/

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 9, 1:16 AM (21 h, 57 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f2/e6/4cd21b48b44047f7588862ca6cba

Event Timeline