diff options
Diffstat (limited to 'src/decrypt.c')
-rw-r--r-- | src/decrypt.c | 88 |
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); } |