gpgme/tests/run-verify.c
Ingo Klöcker 1a9f192ab4
core: Support reading the archive to decrypt/verify directly from a file
* src/engine-gpg.c (add_file_name_arg_or_data): New.
(gpg_decrypt): Use add_file_name_arg_or_data instead of add_data for the
ciphertext.
(gpg_verify): Use add_file_name_arg_or_data instead of add_data for the
signature and the signed text.

* tests/run-decrypt.c (show_usage): New option --direct-file-io.
(main): Parse new option. If option is given, then don't open input
file, create simple data object instead of data object from stream and
set input file name on input data.
* tests/run-verify.c (show_usage): New option --direct-file-io.
(main): Parse new option. If option is given, then don't open input
files, create simple data objects instead of data objects from stream
and set input file names on input data objects.
--

This change makes it possible to tell gpg (and gpgtar) to read the
input (i.e. the signed/encrypted data or the signature or the created
archive) directly from the files with given file names instead of from
streams piped through GpgME's Data IO.

GnuPG-bug-id: 6530
2023-06-21 14:38:50 +02:00

550 lines
16 KiB
C

/* run-verify.c - Helper to perform a verify operation
* Copyright (C) 2009 g10 Code GmbH
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <https://gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
/* We need to include config.h so that we know whether we are building
with large file system (LFS) support. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <gpgme.h>
#define PGM "run-verify"
#include "run-support.h"
static int verbose;
static const char *
isotimestr (unsigned long value)
{
time_t t;
static char buffer[25+5];
struct tm *tp;
if (!value)
return "none";
t = value;
tp = gmtime (&t);
snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
return buffer;
}
static gpg_error_t
status_cb (void *opaque, const char *keyword, const char *value)
{
(void)opaque;
fprintf (stderr, "status_cb: %s %s\n", keyword, value);
return 0;
}
static void
print_summary (gpgme_sigsum_t summary)
{
if ( (summary & GPGME_SIGSUM_VALID ))
fputs (" valid", stdout);
if ( (summary & GPGME_SIGSUM_GREEN ))
fputs (" green", stdout);
if ( (summary & GPGME_SIGSUM_RED ))
fputs (" red", stdout);
if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
fputs (" revoked", stdout);
if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
fputs (" key-expired", stdout);
if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
fputs (" sig-expired", stdout);
if ( (summary & GPGME_SIGSUM_KEY_MISSING))
fputs (" key-missing", stdout);
if ( (summary & GPGME_SIGSUM_CRL_MISSING))
fputs (" crl-missing", stdout);
if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
fputs (" crl-too-old", stdout);
if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
fputs (" bad-policy", stdout);
if ( (summary & GPGME_SIGSUM_SYS_ERROR ))
fputs (" sys-error", stdout);
}
static void
print_validity (gpgme_validity_t val)
{
const char *s = NULL;
switch (val)
{
case GPGME_VALIDITY_UNKNOWN: s = "unknown"; break;
case GPGME_VALIDITY_UNDEFINED:s = "undefined"; break;
case GPGME_VALIDITY_NEVER: s = "never"; break;
case GPGME_VALIDITY_MARGINAL: s = "marginal"; break;
case GPGME_VALIDITY_FULL: s = "full"; break;
case GPGME_VALIDITY_ULTIMATE: s = "ultimate"; break;
}
if (s)
fputs (s, stdout);
else
printf ("[bad validity value %u]", (unsigned int)val);
}
static void
print_description (const char *text, int indent)
{
for (; *text; text++)
{
putchar (*text);
if (*text == '\n')
printf ("%*s", indent, "");
}
putchar ('\n');
}
static void
print_result (gpgme_verify_result_t result)
{
gpgme_signature_t sig;
gpgme_sig_notation_t nt;
gpgme_user_id_t uid;
gpgme_tofu_info_t ti;
int count = 0;
printf ("Original file name .: %s\n", nonnull(result->file_name));
printf ("MIME flag ..........: %s\n", result->is_mime? "yes":"no");
for (sig = result->signatures; sig; sig = sig->next)
{
printf ("Signature ...: %d\n", count++);
printf (" status ....: %s\n", gpgme_strerror (sig->status));
printf (" summary ...:"); print_summary (sig->summary); putchar ('\n');
printf (" fingerprint: %s\n", nonnull (sig->fpr));
printf (" created ...: %lu\n", sig->timestamp);
printf (" expires ...: %lu\n", sig->exp_timestamp);
printf (" validity ..: ");
print_validity (sig->validity); putchar ('\n');
printf (" val.reason : %s\n", gpgme_strerror (sig->status));
printf (" pubkey algo: %d (%s)\n", sig->pubkey_algo,
nonnull(gpgme_pubkey_algo_name (sig->pubkey_algo)));
printf (" digest algo: %d (%s)\n", sig->hash_algo,
nonnull(gpgme_hash_algo_name (sig->hash_algo)));
printf (" pka address: %s\n", nonnull (sig->pka_address));
printf (" pka trust .: %s\n",
sig->pka_trust == 0? "n/a" :
sig->pka_trust == 1? "bad" :
sig->pka_trust == 2? "okay": "RFU");
printf (" other flags:%s%s%s\n",
sig->wrong_key_usage? " wrong-key-usage":"",
sig->chain_model? " chain-model":"",
sig->is_de_vs? " de-vs":""
);
for (nt = sig->notations; nt; nt = nt->next)
{
if (nt->name)
{
printf (" notation ..: '%s'\n", nt->name);
if (strlen (nt->name) != nt->name_len)
printf (" warning .: name larger (%d)\n", nt->name_len);
printf (" flags ...:%s%s (0x%02x)\n",
nt->critical? " critical":"",
nt->human_readable? " human":"",
nt->flags);
if (nt->value)
printf (" value ...: '%s'\n", nt->value);
}
else
{
printf (" policy ....: '%s'\n", nt->value);
}
if ((nt->value?strlen (nt->value):0) != nt->value_len)
printf (" warning .: value larger (%d)\n", nt->value_len);
}
if (sig->key)
{
printf (" primary fpr: %s\n", nonnull (sig->key->fpr));
for (uid = sig->key->uids; uid; uid = uid->next)
{
printf (" tofu addr .: %s\n", nonnull (uid->address));
ti = uid->tofu;
if (!ti)
continue;
printf (" validity : %u (%s)\n", ti->validity,
ti->validity == 0? "conflict" :
ti->validity == 1? "no history" :
ti->validity == 2? "little history" :
ti->validity == 3? "enough history" :
ti->validity == 4? "lot of history" : "?");
printf (" policy ..: %u (%s)\n", ti->policy,
ti->policy == GPGME_TOFU_POLICY_NONE? "none" :
ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" :
ti->policy == GPGME_TOFU_POLICY_GOOD? "good" :
ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" :
ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
printf (" signcount: %hu\n", ti->signcount);
printf (" first..: %s\n", isotimestr (ti->signfirst));
printf (" last ..: %s\n", isotimestr (ti->signlast));
printf (" encrcount: %hu\n", ti->encrcount);
printf (" first..: %s\n", isotimestr (ti->encrfirst));
printf (" last ..: %s\n", isotimestr (ti->encrlast));
printf (" desc ....: ");
print_description (nonnull (ti->description), 15);
}
}
}
}
static int
show_usage (int ex)
{
fputs ("usage: " PGM " [options] [DETACHEDSIGFILE] FILE\n\n"
"Options:\n"
" --verbose run in verbose mode\n"
" --status print status lines from the backend\n"
" --openpgp use the OpenPGP protocol (default)\n"
" --cms use the CMS protocol\n"
" --binary assume binary signature\n"
" --sender MBOX use MBOX as sender address\n"
" --repeat N repeat the operation N times\n"
" --auto-key-retrieve\n"
" --auto-key-import\n"
" --archive extract files from a signed archive FILE\n"
" --directory DIR extract the files into the directory DIR\n"
" --diagnostics print diagnostics\n"
" --direct-file-io pass file names instead of streams with content of files to backend\n"
, stderr);
exit (ex);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
const char *s;
gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
gpgme_verify_flags_t flags = 0;
int print_status = 0;
const char *sender = NULL;
const char *directory = NULL;
int auto_key_retrieve = 0;
int auto_key_import = 0;
gpgme_data_encoding_t encoding = GPGME_DATA_ENCODING_NONE;
int diagnostics = 0;
int direct_file_io = 0;
int repeats = 1;
int i;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
show_usage (0);
else if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--status"))
{
print_status = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--openpgp"))
{
protocol = GPGME_PROTOCOL_OpenPGP;
argc--; argv++;
}
else if (!strcmp (*argv, "--cms"))
{
protocol = GPGME_PROTOCOL_CMS;
argc--; argv++;
}
else if (!strcmp (*argv, "--binary"))
{
encoding = GPGME_DATA_ENCODING_BINARY;
argc--; argv++;
}
else if (!strcmp (*argv, "--sender"))
{
argc--; argv++;
if (!argc)
show_usage (1);
sender = *argv;
argc--; argv++;
}
else if (!strcmp (*argv, "--repeat"))
{
argc--; argv++;
if (!argc)
show_usage (1);
repeats = atoi (*argv);
argc--; argv++;
}
else if (!strcmp (*argv, "--auto-key-retrieve"))
{
auto_key_retrieve = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--auto-key-import"))
{
auto_key_import = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--archive"))
{
flags |= GPGME_VERIFY_ARCHIVE;
argc--; argv++;
}
else if (!strcmp (*argv, "--directory"))
{
argc--; argv++;
if (!argc)
show_usage (1);
directory = *argv;
argc--; argv++;
}
else if (!strcmp (*argv, "--diagnostics"))
{
diagnostics = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--direct-file-io"))
{
direct_file_io = 1;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
show_usage (1);
}
if (argc < 1 || argc > 2 || (argc > 1 && (flags & GPGME_VERIFY_ARCHIVE)))
show_usage (1);
init_gpgme (protocol);
for (i = 0; i < repeats; i++)
{
gpgme_error_t err;
gpgme_ctx_t ctx;
FILE *fp_sig = NULL;
gpgme_data_t sig = NULL;
FILE *fp_msg = NULL;
gpgme_data_t msg = NULL;
gpgme_data_t out = NULL;
gpgme_verify_result_t result;
if (repeats > 1)
{
printf ("Repeat: %i\n", i);
}
if (!direct_file_io)
{
fp_sig = fopen (argv[0], "rb");
if (!fp_sig)
{
err = gpgme_error_from_syserror ();
fprintf (stderr, PGM ": can't open `%s': %s\n",
argv[0], gpgme_strerror (err));
exit (1);
}
if (argc > 1)
{
fp_msg = fopen (argv[1], "rb");
if (!fp_msg)
{
err = gpgme_error_from_syserror ();
fprintf (stderr, PGM ": can't open `%s': %s\n",
argv[1], gpgme_strerror (err));
exit (1);
}
}
}
err = gpgme_new (&ctx);
fail_if_err (err);
gpgme_set_protocol (ctx, protocol);
if (print_status)
{
gpgme_set_status_cb (ctx, status_cb, NULL);
gpgme_set_ctx_flag (ctx, "full-status", "1");
}
/* gpgme_set_ctx_flag (ctx, "raw-description", "1"); */
if (auto_key_retrieve)
{
gpgme_set_ctx_flag (ctx, "auto-key-retrieve", "1");
s = gpgme_get_ctx_flag (ctx, "auto-key-retrieve");
if (!s || strcmp (s, "1"))
{
fprintf (stderr, PGM ": gpgme_get_ctx_flag failed for '%s'\n",
"auto-key-retrieve");
exit (1);
}
}
if (auto_key_import)
{
gpgme_set_ctx_flag (ctx, "auto-key-import", "1");
s = gpgme_get_ctx_flag (ctx, "auto-key-import");
if (!s || strcmp (s, "1"))
{
fprintf (stderr, PGM ": gpgme_get_ctx_flag failed for '%s'\n",
"auto-key-import");
exit (1);
}
}
if (sender)
{
err = gpgme_set_sender (ctx, sender);
fail_if_err (err);
}
if (direct_file_io)
err = gpgme_data_new (&sig);
else
err = gpgme_data_new_from_stream (&sig, fp_sig);
if (err)
{
fprintf (stderr, PGM ": error allocating data object: %s\n",
gpgme_strerror (err));
exit (1);
}
gpgme_data_set_encoding (sig, encoding);
if (direct_file_io)
{
err = gpgme_data_set_file_name (sig, argv[0]);
if (err)
{
fprintf (stderr, PGM ": error setting file name (sig): %s\n",
gpgme_strerror (err));
exit (1);
}
}
if (argc > 1)
{
if (direct_file_io)
err = gpgme_data_new (&msg);
else
err = gpgme_data_new_from_stream (&msg, fp_msg);
if (err)
{
fprintf (stderr, PGM ": error allocating data object: %s\n",
gpgme_strerror (err));
exit (1);
}
if (direct_file_io)
{
err = gpgme_data_set_file_name (msg, argv[1]);
if (err)
{
fprintf (stderr, PGM ": error setting file name (msg): %s\n",
gpgme_strerror (err));
exit (1);
}
}
}
if (directory && (flags & GPGME_VERIFY_ARCHIVE))
{
err = gpgme_data_new (&out);
if (err)
{
fprintf (stderr, PGM ": error allocating data object: %s\n",
gpgme_strerror (err));
exit (1);
}
err = gpgme_data_set_file_name (out, directory);
if (err)
{
fprintf (stderr, PGM ": error setting file name (out): %s\n",
gpgme_strerror (err));
exit (1);
}
}
err = gpgme_op_verify_ext (ctx, flags, sig, msg, out);
result = gpgme_op_verify_result (ctx);
if (diagnostics)
{
gpgme_data_t diag;
gpgme_error_t diag_err;
gpgme_data_new (&diag);
diag_err = gpgme_op_getauditlog (ctx, diag, GPGME_AUDITLOG_DIAG);
if (diag_err)
{
fprintf (stderr, PGM ": getting diagnostics failed: %s\n",
gpgme_strerror (diag_err));
}
else
{
fputs ("Begin Diagnostics:\n", stdout);
print_data (diag);
fputs ("End Diagnostics.\n", stdout);
}
gpgme_data_release (diag);
}
if (result)
print_result (result);
if (err)
{
fprintf (stderr, PGM ": verify failed: %s\n", gpgme_strerror (err));
exit (1);
}
gpgme_data_release (out);
gpgme_data_release (msg);
gpgme_data_release (sig);
gpgme_release (ctx);
if (fp_msg)
fclose (fp_msg);
if (fp_sig)
fclose (fp_sig);
}
return 0;
}