core: Return a better error code on certain decryption failures.

* src/decrypt.c (op_data_t): Add field first_status_error.
(parse_status_error): Set it.
(_gpgme_decrypt_status_handler): Prefer an ERROR code over a
NO_SECKEY.
--

GnuPG-bug-id: 3983
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-06-06 16:20:27 +02:00
parent 998fec8a4f
commit 2c4c569247
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B

View File

@ -53,7 +53,7 @@ typedef struct
* status lines for each key the message has been encrypted to but * status lines for each key the message has been encrypted to but
* that secret key is not available. This can't be done for hidden * that secret key is not available. This can't be done for hidden
* recipients, though. We track it here to allow for a better error * recipients, though. We track it here to allow for a better error
* message that the general DECRYPTION_FAILED. */ * message than the general DECRYPTION_FAILED. */
int any_no_seckey; int any_no_seckey;
/* If the engine emits a DECRYPTION_INFO status and that does not /* If the engine emits a DECRYPTION_INFO status and that does not
@ -61,6 +61,10 @@ typedef struct
* is set. */ * is set. */
int not_integrity_protected; int not_integrity_protected;
/* The error code from the first ERROR line. This is in some cases
* used to return a better matching error code to the caller. */
gpg_error_t first_status_error;
/* A pointer to the next pointer of the last recipient in the list. /* A pointer to the next pointer of the last recipient in the list.
This makes appending new invalid signers painless while This makes appending new invalid signers painless while
preserving the order. */ preserving the order. */
@ -222,6 +226,10 @@ parse_status_error (char *args, op_data_t opd)
opd->not_integrity_protected = 1; opd->not_integrity_protected = 1;
} }
/* Record the first error code. */
if (err && !opd->first_status_error)
opd->first_status_error = err;
free (args2); free (args2);
return 0; return 0;
@ -360,17 +368,43 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
* only a warning. * only a warning.
* Fixme: These error values should probably be attributed to * Fixme: These error values should probably be attributed to
* the underlying crypto engine (as error source). */ * the underlying crypto engine (as error source). */
if (opd->failed && opd->pkdecrypt_failed) if (opd->failed)
{
/* This comes from a specialized ERROR status line. */
if (opd->pkdecrypt_failed)
return opd->pkdecrypt_failed; return opd->pkdecrypt_failed;
else if (opd->failed && opd->any_no_seckey)
return gpg_error (GPG_ERR_NO_SECKEY); /* For an integrity failure return just DECRYPTION_FAILED;
else if (opd->failed || (opd->not_integrity_protected * the actual cause can be taken from an already set
&& !ctx->ignore_mdc_error)) * decryption result flag. */
if ((opd->not_integrity_protected && !ctx->ignore_mdc_error))
return gpg_error (GPG_ERR_DECRYPT_FAILED); return gpg_error (GPG_ERR_DECRYPT_FAILED);
/* If we have any other ERROR code we prefer that over
* NO_SECKEY because it is probably the better matching
* code. For example a garbled message with multiple
* plaintext will return BAD_DATA here but may also have
* indicated a NO_SECKEY. */
if (opd->first_status_error)
return opd->first_status_error;
/* No secret key is pretty common reason. */
if (opd->any_no_seckey)
return gpg_error (GPG_ERR_NO_SECKEY);
/* Generic decryption failed error code. */
return gpg_error (GPG_ERR_DECRYPT_FAILED);
}
else if (!opd->okay) else if (!opd->okay)
{
/* No data was found. */
return gpg_error (GPG_ERR_NO_DATA); return gpg_error (GPG_ERR_NO_DATA);
}
else if (opd->failure_code) else if (opd->failure_code)
{
/* The engine returned failure code at program exit. */
return opd->failure_code; return opd->failure_code;
}
break; break;
case GPGME_STATUS_DECRYPTION_INFO: case GPGME_STATUS_DECRYPTION_INFO:
@ -389,8 +423,9 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
case GPGME_STATUS_ERROR: case GPGME_STATUS_ERROR:
/* Note that this is an informational status code which should /* Note that this is an informational status code which should
not lead to an error return unless it is something not * not lead to an error return unless it is something not
related to the backend. */ * related to the backend. However, it is used to return a
* better matching final error code. */
err = parse_status_error (args, opd); err = parse_status_error (args, opd);
if (err) if (err)
return err; return err;