diff options
author | Anas Nashif <anas.nashif@intel.com> | 2013-02-19 08:22:18 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2013-02-19 08:22:18 -0800 |
commit | 26fb537f9cf011eaeaf975adcad5e8e9154d04fd (patch) | |
tree | ddc2171273fca8b730b9c496e1b5ed3b01878577 /src/decrypt.c | |
download | gpgme-26fb537f9cf011eaeaf975adcad5e8e9154d04fd.tar.gz gpgme-26fb537f9cf011eaeaf975adcad5e8e9154d04fd.tar.bz2 gpgme-26fb537f9cf011eaeaf975adcad5e8e9154d04fd.zip |
Imported Upstream version 1.3.2upstream/1.3.2
Diffstat (limited to 'src/decrypt.c')
-rw-r--r-- | src/decrypt.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/src/decrypt.c b/src/decrypt.c new file mode 100644 index 0000000..43945ec --- /dev/null +++ b/src/decrypt.c @@ -0,0 +1,403 @@ +/* decrypt.c - Decrypt function. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002, 2003, 2004 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "debug.h" +#include "gpgme.h" +#include "util.h" +#include "context.h" +#include "ops.h" + + + +typedef struct +{ + struct _gpgme_op_decrypt_result result; + + int okay; + int failed; + + /* 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; +} *op_data_t; + + +static void +release_op_data (void *hook) +{ + op_data_t opd = (op_data_t) hook; + gpgme_recipient_t recipient = opd->result.recipients; + + if (opd->result.unsupported_algorithm) + free (opd->result.unsupported_algorithm); + + if (opd->result.file_name) + free (opd->result.file_name); + + while (recipient) + { + gpgme_recipient_t next = recipient->next; + free (recipient); + recipient = next; + } +} + + +gpgme_decrypt_result_t +gpgme_op_decrypt_result (gpgme_ctx_t ctx) +{ + void *hook; + op_data_t opd; + gpgme_error_t err; + + TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx); + + err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); + opd = hook; + if (err || !opd) + { + TRACE_SUC0 ("result=(null)"); + return NULL; + } + + if (_gpgme_debug_trace ()) + { + gpgme_recipient_t rcp; + + if (opd->result.unsupported_algorithm) + { + TRACE_LOG1 ("result: unsupported_algorithm: %s", + opd->result.unsupported_algorithm); + } + if (opd->result.wrong_key_usage) + { + TRACE_LOG ("result: wrong key usage"); + } + rcp = opd->result.recipients; + while (rcp) + { + TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, " + "status=%s", rcp->keyid, rcp->pubkey_algo, + gpg_strerror (rcp->status)); + rcp = rcp->next; + } + if (opd->result.file_name) + { + TRACE_LOG1 ("result: original file name: %s", opd->result.file_name); + } + } + + TRACE_SUC1 ("result=%p", &opd->result); + return &opd->result; +} + + +static gpgme_error_t +parse_enc_to (char *args, gpgme_recipient_t *recp) +{ + gpgme_recipient_t rec; + char *tail; + int i; + + rec = malloc (sizeof (*rec)); + if (!rec) + return gpg_error_from_syserror (); + + rec->next = NULL; + rec->keyid = rec->_keyid; + rec->status = 0; + + for (i = 0; i < sizeof (rec->_keyid) - 1; i++) + { + if (args[i] == '\0' || args[i] == ' ') + break; + + rec->_keyid[i] = args[i]; + } + rec->_keyid[i] = '\0'; + + args = &args[i]; + if (*args != '\0' && *args != ' ') + { + free (rec); + return gpg_error (GPG_ERR_INV_ENGINE); + } + + while (*args == ' ') + args++; + + if (*args) + { + gpg_err_set_errno (0); + rec->pubkey_algo = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') + { + /* The crypto backend does not behave. */ + free (rec); + return gpg_error (GPG_ERR_INV_ENGINE); + } + } + + /* FIXME: The key length is always 0 right now, so no need to parse + it. */ + + *recp = rec; + return 0; +} + + +gpgme_error_t +_gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, + char *args) +{ + gpgme_ctx_t ctx = (gpgme_ctx_t) priv; + gpgme_error_t err; + void *hook; + op_data_t opd; + + err = _gpgme_passphrase_status_handler (priv, code, args); + if (err) + return err; + + err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); + opd = hook; + if (err) + return err; + + switch (code) + { + case GPGME_STATUS_EOF: + /* FIXME: These error values should probably be attributed to + the underlying crypto engine (as error source). */ + if (opd->failed) + return gpg_error (GPG_ERR_DECRYPT_FAILED); + else if (!opd->okay) + return gpg_error (GPG_ERR_NO_DATA); + break; + + case GPGME_STATUS_DECRYPTION_INFO: + /* Fixme: Provide a way to return the used symmetric algorithm. */ + break; + + case GPGME_STATUS_DECRYPTION_OKAY: + opd->okay = 1; + break; + + case GPGME_STATUS_DECRYPTION_FAILED: + opd->failed = 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. */ + { + const char d_alg[] = "decrypt.algorithm"; + const char k_alg[] = "decrypt.keyusage"; + + if (!strncmp (args, d_alg, sizeof (d_alg) - 1)) + { + args += sizeof (d_alg) - 1; + while (*args == ' ') + args++; + + if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM) + { + char *end; + + while (*args && *args != ' ') + args++; + while (*args == ' ') + args++; + + end = strchr (args, ' '); + if (end) + *end = '\0'; + + if (!(*args == '?' && *(args + 1) == '\0')) + { + opd->result.unsupported_algorithm = strdup (args); + if (!opd->result.unsupported_algorithm) + return gpg_error_from_syserror (); + } + } + } + else if (!strncmp (args, k_alg, sizeof (k_alg) - 1)) + { + args += sizeof (k_alg) - 1; + while (*args == ' ') + args++; + + if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE) + opd->result.wrong_key_usage = 1; + } + } + break; + + case GPGME_STATUS_ENC_TO: + err = parse_enc_to (args, opd->last_recipient_p); + if (err) + return err; + + opd->last_recipient_p = &(*opd->last_recipient_p)->next; + break; + + case GPGME_STATUS_NO_SECKEY: + { + gpgme_recipient_t rec = opd->result.recipients; + + while (rec) + { + if (!strcmp (rec->keyid, args)) + { + rec->status = gpg_error (GPG_ERR_NO_SECKEY); + break; + } + rec = rec->next; + } + /* FIXME: Is this ok? */ + if (!rec) + return gpg_error (GPG_ERR_INV_ENGINE); + } + break; + + case GPGME_STATUS_PLAINTEXT: + err = _gpgme_parse_plaintext (args, &opd->result.file_name); + if (err) + return err; + + default: + break; + } + + return 0; +} + + +static gpgme_error_t +decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) +{ + gpgme_error_t err; + + err = _gpgme_progress_status_handler (priv, code, args); + if (!err) + err = _gpgme_decrypt_status_handler (priv, code, args); + return err; +} + + +gpgme_error_t +_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx) +{ + gpgme_error_t err; + void *hook; + op_data_t opd; + + err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, + sizeof (*opd), release_op_data); + opd = hook; + if (err) + return err; + + opd->last_recipient_p = &opd->result.recipients; + return 0; +} + + +static gpgme_error_t +decrypt_start (gpgme_ctx_t ctx, int synchronous, + gpgme_data_t cipher, gpgme_data_t plain) +{ + gpgme_error_t err; + + err = _gpgme_op_reset (ctx, synchronous); + if (err) + return err; + + err = _gpgme_op_decrypt_init_result (ctx); + if (err) + return err; + + if (!cipher) + return gpg_error (GPG_ERR_NO_DATA); + if (!plain) + return gpg_error (GPG_ERR_INV_VALUE); + + if (err) + return err; + + if (ctx->passphrase_cb) + { + err = _gpgme_engine_set_command_handler + (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); + if (err) + return err; + } + + _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx); + + return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain); +} + + +gpgme_error_t +gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, + gpgme_data_t plain) +{ + gpgme_error_t err; + + TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx, + "cipher=%p, plain=%p", cipher, plain); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + + err = decrypt_start (ctx, 0, cipher, plain); + return TRACE_ERR (err); +} + + +/* Decrypt ciphertext CIPHER within CTX and store the resulting + plaintext in PLAIN. */ +gpgme_error_t +gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) +{ + gpgme_error_t err; + + TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx, + "cipher=%p, plain=%p", cipher, plain); + + if (!ctx) + return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + + err = decrypt_start (ctx, 1, cipher, plain); + if (!err) + err = _gpgme_wait_one (ctx); + return TRACE_ERR (err); +} |