core: Add gpg auditlog to get diagnostics

* src/engine-gpg.c (engine_gpg): Add diagnostics member.
(gpg_release): Release diagnostics data.
(gpg_new): Set up logger-fd and diagnostics.
(gpg_getauditlog): New. Copy diagnostics to a user data.
(engine_ops): Add getauditlog.
* src/engine-gpgsm.c (gpgsm_getauditlog): Return not implemented
for GPGME_AUDITLOG_DIAG.
* src/getauditlog.c (getauditlog_start): Don't reset engine
for diagnostics.
* src/gpgme.h.in (GPGME_AUDITLOG_DIAG): New.
(GPGME_AUDITLOG_DEFAULT): New alias to 0.
* tests/run-decrypt.c (show_usage, main): Add --diagnostics.
* doc/gpgme.texi(Additional Logs): Document getauditlog.

--
This enables users of GPGME to get more verbose information
from gpg which can assist users in figuring out a problem
that was before hidden behind a generalized error like
"Decryption Failed".

For GPGSM it is not yet available as it is problematic to
get it properly in server mode and GPGSM already had the
original audit log mechanism in place.

GPGME_AUDITLOG_DEFAULT was added for a more explicit
documentation.
This commit is contained in:
Andre Heinecke 2018-07-04 15:39:01 +02:00
parent 7d65dc2a5c
commit a2458806f8
No known key found for this signature in database
GPG Key ID: 2978E9D40CBABA5C
7 changed files with 168 additions and 4 deletions

2
NEWS
View File

@ -8,6 +8,8 @@ Noteworthy changes in version 1.11.2 (unreleased)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgme_decrypt_result_t EXTENDED: New field legacy_cipher_nomdc. gpgme_decrypt_result_t EXTENDED: New field legacy_cipher_nomdc.
gpgme_set_ctx_flag EXTENDED: New flag 'ignore-mdc-error'. gpgme_set_ctx_flag EXTENDED: New flag 'ignore-mdc-error'.
GPGME_AUDITLOG_DEFAULT NEW.
GPGME_AUDITLOG_DIAG NEW.
cpp: DecryptionResult::sessionKey NEW. cpp: DecryptionResult::sessionKey NEW.
cpp: DecryptionResult::symkeyAlgo NEW. cpp: DecryptionResult::symkeyAlgo NEW.
cpp: DecryptionResult::isLegacyCipherNoMDC New. cpp: DecryptionResult::isLegacyCipherNoMDC New.

View File

@ -2426,6 +2426,7 @@ started. In fact, these references are accessed through the
* Progress Meter Callback:: Being informed about the progress. * Progress Meter Callback:: Being informed about the progress.
* Status Message Callback:: Status messages received from gpg. * Status Message Callback:: Status messages received from gpg.
* Locale:: Setting the locale of a context. * Locale:: Setting the locale of a context.
* Additional Logs:: Additional logs of a context.
@end menu @end menu
@ -3155,6 +3156,70 @@ The function returns an error if not enough memory is available.
@end deftypefun @end deftypefun
@node Additional Logs
@subsection Additional Logs
@cindex auditlog, of the engine
@cindex auditlog
Additional logs can be associated with a context. These logs are
engine specific and can be be obtained with @code{gpgme_op_getauditlog}.
@deftypefun gpgme_error_t gpgme_op_getauditlog @
(@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_data_t @var{output}}, @
@w{unsigned int @var{flags}})
@since{1.1.1}
The function @code{gpgme_op_getauditlog} is used to obtain additional
logs as specified by @var{flags} into the @var{output} data. If
The function returns the error code @code{GPG_ERR_NO_ERROR} if a
log could be queried from the engine, and @code{GPG_ERR_NOT_IMPLEMENTED}
if the log specified in @var{flags} is not available for this engine.
If no log is available @code{GPG_ERR_NO_DATA} is returned.
The value in @var{flags} is a bitwise-or combination of one or
multiple of the following bit values:
@table @code
@item GPGME_AUDITLOG_DIAG
@since{1.11.2}
Obtain diagnostic output which would be written to @code{stderr} in
interactive use of the engine. This can be used to provide additional
diagnostic information in case of errors in other operations.
Note: If log-file has been set in the configuration the log will
be empty and @code{GPG_ERR_NO_DATA} will be returned.
Implemented for: @code{GPGME_PROTOCOL_OpenPGP}
@item GPGME_AUDITLOG_DEFAULT
@since{1.11.2}
This flag has the value 0 for compatibility reasons. Obtains additional
information from the engine by issuing the @code{GETAUDITLOG} command.
For @code{GPGME_PROTOCOL_CMS} this provides additional information about
the X509 certificate chain.
Implemented for: @code{GPGME_PROTOCOL_CMS}
@item GPGME_AUDITLOG_HTML
@since{1.1.1}
Same as @code{GPGME_AUDITLOG_DEFAULT} but in HTML.
Implemented for: @code{GPGME_PROTOCOL_CMS}
@end table
@end deftypefun
@deftypefun gpgme_error_t gpgme_op_getauditlog_start @
(@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_data_t @var{output}}, @
@w{unsigned int @var{flags}})
@since{1.1.1}
This is the asynchronous variant of @code{gpgme_op_getauditlog}.
@end deftypefun
@node Key Management @node Key Management
@section Key Management @section Key Management
@cindex key management @cindex key management

View File

@ -149,6 +149,9 @@ struct engine_gpg
/* NULL or the data object fed to --override_session_key-fd. */ /* NULL or the data object fed to --override_session_key-fd. */
gpgme_data_t override_session_key; gpgme_data_t override_session_key;
/* Memory data containing diagnostics (--logger-fd) of gpg */
gpgme_data_t diagnostics;
}; };
typedef struct engine_gpg *engine_gpg_t; typedef struct engine_gpg *engine_gpg_t;
@ -452,6 +455,7 @@ gpg_release (void *engine)
free (gpg->cmd.keyword); free (gpg->cmd.keyword);
gpgme_data_release (gpg->override_session_key); gpgme_data_release (gpg->override_session_key);
gpgme_data_release (gpg->diagnostics);
free (gpg); free (gpg);
} }
@ -620,6 +624,16 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
} }
} }
rc = gpgme_data_new (&gpg->diagnostics);
if (rc)
goto leave;
rc = add_arg (gpg, "--logger-fd");
if (rc)
goto leave;
rc = add_data (gpg, gpg->diagnostics, -2, 1);
leave: leave:
if (rc) if (rc)
gpg_release (gpg); gpg_release (gpg);
@ -3243,6 +3257,52 @@ gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode)
} }
static gpgme_error_t
gpg_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
{
engine_gpg_t gpg = engine;
#define MYBUFLEN 4096
char buf[MYBUFLEN];
int nread;
int any_written = 0;
if (!(flags & GPGME_AUDITLOG_DIAG))
{
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
if (!gpg || !output)
{
return gpg_error (GPG_ERR_INV_VALUE);
}
if (!gpg->diagnostics)
{
return gpg_error (GPG_ERR_GENERAL);
}
gpgme_data_rewind (gpg->diagnostics);
while ((nread = gpgme_data_read (gpg->diagnostics, buf, MYBUFLEN)) > 0)
{
any_written = 1;
if (gpgme_data_write (output, buf, nread) == -1)
return gpg_error_from_syserror ();
}
if (!any_written)
{
return gpg_error (GPG_ERR_NO_DATA);
}
if (nread == -1)
return gpg_error_from_syserror ();
gpgme_data_rewind (output);
return 0;
#undef MYBUFLEN
}
struct engine_ops _gpgme_engine_ops_gpg = struct engine_ops _gpgme_engine_ops_gpg =
{ {
@ -3280,7 +3340,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
gpg_sign, gpg_sign,
gpg_trustlist, gpg_trustlist,
gpg_verify, gpg_verify,
NULL, /* getauditlog */ gpg_getauditlog,
NULL, /* opassuan_transact */ NULL, /* opassuan_transact */
NULL, /* conf_load */ NULL, /* conf_load */
NULL, /* conf_save */ NULL, /* conf_save */

View File

@ -2064,6 +2064,9 @@ gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
if (!gpgsm || !output) if (!gpgsm || !output)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if ((flags & GPGME_AUDITLOG_DIAG))
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#if USE_DESCRIPTOR_PASSING #if USE_DESCRIPTOR_PASSING
gpgsm->output_cb.data = output; gpgsm->output_cb.data = output;
err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);

View File

@ -47,9 +47,12 @@ getauditlog_start (gpgme_ctx_t ctx, int synchronous,
if (!output) if (!output)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (!(flags & GPGME_AUDITLOG_DIAG))
{
err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) ); err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) );
if (err) if (err)
return err; return err;
}
_gpgme_engine_set_status_handler (ctx->engine, _gpgme_engine_set_status_handler (ctx->engine,
getauditlog_status_handler, ctx); getauditlog_status_handler, ctx);

View File

@ -404,7 +404,9 @@ typedef unsigned int gpgme_export_mode_t;
/* Flags for the audit log functions. */ /* Flags for the audit log functions. */
#define GPGME_AUDITLOG_DEFAULT 0
#define GPGME_AUDITLOG_HTML 1 #define GPGME_AUDITLOG_HTML 1
#define GPGME_AUDITLOG_DIAG 2
#define GPGME_AUDITLOG_WITH_HELP 128 #define GPGME_AUDITLOG_WITH_HELP 128

View File

@ -88,6 +88,7 @@ show_usage (int ex)
" --no-symkey-cache disable the use of that cache\n" " --no-symkey-cache disable the use of that cache\n"
" --ignore-mdc-error allow decryption of legacy data\n" " --ignore-mdc-error allow decryption of legacy data\n"
" --unwrap remove only the encryption layer\n" " --unwrap remove only the encryption layer\n"
" --diagnostics print diagnostics\n"
, stderr); , stderr);
exit (ex); exit (ex);
} }
@ -112,6 +113,7 @@ main (int argc, char **argv)
int no_symkey_cache = 0; int no_symkey_cache = 0;
int ignore_mdc_error = 0; int ignore_mdc_error = 0;
int raw_output = 0; int raw_output = 0;
int diagnostics = 0;
if (argc) if (argc)
{ argc--; argv++; } { argc--; argv++; }
@ -177,6 +179,11 @@ main (int argc, char **argv)
ignore_mdc_error = 1; ignore_mdc_error = 1;
argc--; argv++; argc--; argv++;
} }
else if (!strcmp (*argv, "--diagnostics"))
{
diagnostics = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--unwrap")) else if (!strcmp (*argv, "--unwrap"))
{ {
flags |= GPGME_DECRYPT_UNWRAP; flags |= GPGME_DECRYPT_UNWRAP;
@ -283,6 +290,28 @@ main (int argc, char **argv)
err = gpgme_op_decrypt_ext (ctx, flags, in, out); err = gpgme_op_decrypt_ext (ctx, flags, in, out);
result = gpgme_op_decrypt_result (ctx); result = gpgme_op_decrypt_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 (err) if (err)
{ {
fprintf (stderr, PGM ": decrypt failed: %s\n", gpgme_strerror (err)); fprintf (stderr, PGM ": decrypt failed: %s\n", gpgme_strerror (err));