aboutsummaryrefslogtreecommitdiffstats
path: root/src/decrypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decrypt.c')
-rw-r--r--src/decrypt.c88
1 files changed, 72 insertions, 16 deletions
diff --git a/src/decrypt.c b/src/decrypt.c
index ecd9c144..b51603a3 100644
--- a/src/decrypt.c
+++ b/src/decrypt.c
@@ -32,7 +32,7 @@
#include "util.h"
#include "context.h"
#include "ops.h"
-
+#include "data.h"
typedef struct
@@ -53,18 +53,25 @@ typedef struct
* 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
* 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;
/* If the engine emits a DECRYPTION_INFO status and that does not
- * indicate that an integrity proetction mode is active, this flag
+ * indicate that an integrity protection mode is active, this flag
* is set. */
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.
This makes appending new invalid signers painless while
preserving the order. */
gpgme_recipient_t *last_recipient_p;
+
+ /* The data object serial number of the plaintext. */
+ uint64_t plaintext_dserial;
} *op_data_t;
@@ -97,6 +104,8 @@ gpgme_op_decrypt_result (gpgme_ctx_t ctx)
TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
+ ctx->ignore_mdc_error = 0; /* Always reset this flag. */
+
err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
opd = hook;
if (err || !opd)
@@ -214,6 +223,15 @@ parse_status_error (char *args, op_data_t opd)
break;
}
}
+ else if (!strcmp (field[0], "nomdc_with_legacy_cipher"))
+ {
+ opd->result.legacy_cipher_nomdc = 1;
+ opd->not_integrity_protected = 1;
+ }
+
+ /* Record the first error code. */
+ if (err && !opd->first_status_error)
+ opd->first_status_error = err;
free (args2);
@@ -353,16 +371,43 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
* only a warning.
* Fixme: These error values should probably be attributed to
* the underlying crypto engine (as error source). */
- if (opd->failed && opd->pkdecrypt_failed)
- return opd->pkdecrypt_failed;
- else if (opd->failed && opd->any_no_seckey)
- return gpg_error (GPG_ERR_NO_SECKEY);
- else if (opd->failed || opd->not_integrity_protected)
- return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ if (opd->failed)
+ {
+ /* This comes from a specialized ERROR status line. */
+ if (opd->pkdecrypt_failed)
+ return opd->pkdecrypt_failed;
+
+ /* For an integrity failure return just DECRYPTION_FAILED;
+ * the actual cause can be taken from an already set
+ * decryption result flag. */
+ if ((opd->not_integrity_protected && !ctx->ignore_mdc_error))
+ 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)
- return gpg_error (GPG_ERR_NO_DATA);
+ {
+ /* No data was found. */
+ return gpg_error (GPG_ERR_NO_DATA);
+ }
else if (opd->failure_code)
- return opd->failure_code;
+ {
+ /* The engine returned failure code at program exit. */
+ return opd->failure_code;
+ }
break;
case GPGME_STATUS_DECRYPTION_INFO:
@@ -377,12 +422,21 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
case GPGME_STATUS_DECRYPTION_FAILED:
opd->failed = 1;
+ /* Tell the data object that it shall not return any data. We
+ * use the serial number because the data object may be owned by
+ * another thread. We also don't check for an error because it
+ * is possible that the data object has already been destroyed
+ * and we are then not interested in returning an error. */
+ if (!ctx->ignore_mdc_error)
+ _gpgme_data_set_prop (NULL, opd->plaintext_dserial,
+ DATA_PROP_BLANKOUT, 1);
break;
case GPGME_STATUS_ERROR:
/* Note that this is an informational status code which should
- not lead to an error return unless it is something not
- related to the backend. */
+ * not lead to an error return unless it is something not
+ * related to the backend. However, it is used to return a
+ * better matching final error code. */
err = parse_status_error (args, opd);
if (err)
return err;
@@ -465,7 +519,7 @@ decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
gpgme_error_t
-_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
+_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, gpgme_data_t plaintext)
{
gpgme_error_t err;
void *hook;
@@ -478,6 +532,7 @@ _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
return err;
opd->last_recipient_p = &opd->result.recipients;
+ opd->plaintext_dserial = _gpgme_data_get_dserial (plaintext);
return 0;
}
@@ -495,7 +550,7 @@ _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous,
if (err)
return err;
- err = _gpgme_op_decrypt_init_result (ctx);
+ err = _gpgme_op_decrypt_init_result (ctx, plain);
if (err)
return err;
@@ -510,7 +565,7 @@ _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous,
if (ctx->passphrase_cb)
{
err = _gpgme_engine_set_command_handler
- (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx);
if (err)
return err;
}
@@ -559,5 +614,6 @@ gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain);
if (!err)
err = _gpgme_wait_one (ctx);
+ ctx->ignore_mdc_error = 0; /* Always reset. */
return TRACE_ERR (err);
}