diff options
Diffstat (limited to 'camel/camel-gpg-context.c')
-rw-r--r-- | camel/camel-gpg-context.c | 1501 |
1 files changed, 0 insertions, 1501 deletions
diff --git a/camel/camel-gpg-context.c b/camel/camel-gpg-context.c deleted file mode 100644 index 9b586e5f9b..0000000000 --- a/camel/camel-gpg-context.c +++ /dev/null @@ -1,1501 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors: Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright 2002 Ximian, Inc. (www.ximian.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. - * - */ - - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <sys/time.h> -#include <termios.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> - -#include "camel-gpg-context.h" -#include "camel-stream-fs.h" -#include "camel-operation.h" - -#define d(x) x - -static void camel_gpg_context_class_init (CamelGpgContextClass *klass); -static void camel_gpg_context_init (CamelGpgContext *obj); -static void camel_gpg_context_finalise (CamelObject *obj); - -static int gpg_sign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash, - CamelStream *istream, CamelStream *ostream, CamelException *ex); -static CamelCipherValidity *gpg_verify (CamelCipherContext *context, CamelCipherHash hash, - CamelStream *istream, CamelStream *sigstream, - CamelException *ex); -static int gpg_encrypt (CamelCipherContext *context, gboolean sign, const char *userid, - GPtrArray *recipients, CamelStream *istream, CamelStream *ostream, - CamelException *ex); -static int gpg_decrypt (CamelCipherContext *context, CamelStream *istream, - CamelStream *ostream, CamelException *ex); - -static const char *gpg_hash_to_id (CamelCipherContext *context, CamelCipherHash hash); -static CamelCipherHash gpg_id_to_hash (CamelCipherContext *context, const char *id); - - -static CamelCipherContextClass *parent_class = NULL; - - -CamelType -camel_gpg_context_get_type (void) -{ - static CamelType type = CAMEL_INVALID_TYPE; - - if (type == CAMEL_INVALID_TYPE) { - type = camel_type_register (camel_cipher_context_get_type (), - "CamelGpgContext", - sizeof (CamelGpgContext), - sizeof (CamelGpgContextClass), - (CamelObjectClassInitFunc) camel_gpg_context_class_init, - NULL, - (CamelObjectInitFunc) camel_gpg_context_init, - (CamelObjectFinalizeFunc) camel_gpg_context_finalise); - } - - return type; -} - -static void -camel_gpg_context_class_init (CamelGpgContextClass *klass) -{ - CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS (klass); - - parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ())); - - cipher_class->sign = gpg_sign; - cipher_class->verify = gpg_verify; - cipher_class->encrypt = gpg_encrypt; - cipher_class->decrypt = gpg_decrypt; - cipher_class->hash_to_id = gpg_hash_to_id; - cipher_class->id_to_hash = gpg_id_to_hash; -} - -static void -camel_gpg_context_init (CamelGpgContext *context) -{ - CamelCipherContext *cipher = (CamelCipherContext *) context; - - context->path = NULL; - context->always_trust = FALSE; - - cipher->sign_protocol = "application/pgp-signature"; - cipher->encrypt_protocol = "application/pgp-encrypted"; -} - -static void -camel_gpg_context_finalise (CamelObject *object) -{ - CamelGpgContext *ctx = (CamelGpgContext *) object; - - g_free (ctx->path); -} - - -/** - * camel_gpg_context_new: - * @session: session - * @path: path to gpg binary - * - * Creates a new gpg cipher context object. - * - * Returns a new gpg cipher context object. - **/ -CamelCipherContext * -camel_gpg_context_new (CamelSession *session, const char *path) -{ - CamelCipherContext *cipher; - CamelGpgContext *ctx; - - g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); - g_return_val_if_fail (path != NULL, NULL); - - ctx = (CamelGpgContext *) camel_object_new (camel_gpg_context_get_type ()); - ctx->path = g_strdup (path); - - cipher = (CamelCipherContext *) ctx; - cipher->session = session; - camel_object_ref (session); - - return cipher; -} - - -/** - * camel_gpg_context_set_always_trust: - * @ctx: gpg context - * @always_trust always truct flag - * - * Sets the @always_trust flag on the gpg context which is used for - * encryption. - **/ -void -camel_gpg_context_set_always_trust (CamelGpgContext *ctx, gboolean always_trust) -{ - g_return_if_fail (CAMEL_IS_GPG_CONTEXT (ctx)); - - ctx->always_trust = always_trust; -} - - -static const char * -gpg_hash_to_id (CamelCipherContext *context, CamelCipherHash hash) -{ - switch (hash) { - case CAMEL_CIPHER_HASH_MD2: - return "pgp-md2"; - case CAMEL_CIPHER_HASH_MD5: - return "pgp-md5"; - case CAMEL_CIPHER_HASH_SHA1: - case CAMEL_CIPHER_HASH_DEFAULT: - return "pgp-sha1"; - case CAMEL_CIPHER_HASH_RIPEMD160: - return "pgp-ripemd160"; - } - - return NULL; -} - -static CamelCipherHash -gpg_id_to_hash (CamelCipherContext *context, const char *id) -{ - if (!strcmp (id, "pgp-md2")) - return CAMEL_CIPHER_HASH_MD2; - else if (!strcmp (id, "pgp-md5")) - return CAMEL_CIPHER_HASH_MD5; - else if (!strcmp (id, "pgp-sha1")) - return CAMEL_CIPHER_HASH_SHA1; - else if (!strcmp (id, "pgp-ripemd160")) - return CAMEL_CIPHER_HASH_RIPEMD160; - - return CAMEL_CIPHER_HASH_DEFAULT; -} - - -enum _GpgCtxMode { - GPG_CTX_MODE_SIGN, - GPG_CTX_MODE_VERIFY, - GPG_CTX_MODE_ENCRYPT, - GPG_CTX_MODE_DECRYPT, -}; - -enum _GpgTrustMetric { - GPG_TRUST_UNKNOWN, - GPG_TRUST_NEVER, - GPG_TRUST_MARGINAL, - GPG_TRUST_FULLY, - GPG_TRUST_ULTIMATE -}; - -struct _GpgCtx { - enum _GpgCtxMode mode; - CamelSession *session; - GHashTable *userid_hint; - pid_t pid; - - char *path; - char *userid; - char *sigfile; - GPtrArray *recipients; - CamelCipherHash hash; - - int stdin_fd; - int stdout_fd; - int stderr_fd; - int status_fd; - int passwd_fd; /* only needed for sign/decrypt */ - - /* status-fd buffer */ - unsigned char *statusbuf; - unsigned char *statusptr; - unsigned int statusleft; - - char *passwd; - - CamelStream *istream; - CamelStream *ostream; - - GByteArray *diagnostics; - - int exit_status; - - unsigned int exited:1; - unsigned int complete:1; - unsigned int seen_eof1:1; - unsigned int seen_eof2:1; - unsigned int always_trust:1; - unsigned int armor:1; - unsigned int need_passwd:1; - unsigned int send_passwd:1; - - unsigned int bad_passwds:2; - - unsigned int validsig:1; - unsigned int trust:3; - - unsigned int padding:18; -}; - -static struct _GpgCtx * -gpg_ctx_new (CamelSession *session, const char *path) -{ - struct _GpgCtx *gpg; - - gpg = g_new (struct _GpgCtx, 1); - gpg->mode = GPG_CTX_MODE_SIGN; - gpg->session = session; - camel_object_ref (CAMEL_OBJECT (session)); - gpg->userid_hint = g_hash_table_new (g_str_hash, g_str_equal); - gpg->complete = FALSE; - gpg->seen_eof1 = FALSE; - gpg->seen_eof2 = FALSE; - gpg->pid = (pid_t) -1; - gpg->exit_status = 0; - gpg->exited = FALSE; - - gpg->path = g_strdup (path); - gpg->userid = NULL; - gpg->sigfile = NULL; - gpg->recipients = NULL; - gpg->hash = CAMEL_CIPHER_HASH_DEFAULT; - gpg->always_trust = FALSE; - gpg->armor = FALSE; - - gpg->stdin_fd = -1; - gpg->stdout_fd = -1; - gpg->stderr_fd = -1; - gpg->status_fd = -1; - gpg->passwd_fd = -1; - - gpg->statusbuf = g_malloc (128); - gpg->statusptr = gpg->statusbuf; - gpg->statusleft = 128; - - gpg->bad_passwds = 0; - gpg->need_passwd = FALSE; - gpg->send_passwd = FALSE; - gpg->passwd = NULL; - - gpg->validsig = FALSE; - gpg->trust = GPG_TRUST_UNKNOWN; - - gpg->istream = NULL; - gpg->ostream = NULL; - - gpg->diagnostics = g_byte_array_new (); - - return gpg; -} - -static void -gpg_ctx_set_mode (struct _GpgCtx *gpg, enum _GpgCtxMode mode) -{ - gpg->mode = mode; - gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT)); -} - -static void -gpg_ctx_set_hash (struct _GpgCtx *gpg, CamelCipherHash hash) -{ - gpg->hash = hash; -} - -static void -gpg_ctx_set_always_trust (struct _GpgCtx *gpg, gboolean trust) -{ - gpg->always_trust = trust; -} - -static void -gpg_ctx_set_userid (struct _GpgCtx *gpg, const char *userid) -{ - g_free (gpg->userid); - gpg->userid = g_strdup (userid); -} - -static void -gpg_ctx_add_recipient (struct _GpgCtx *gpg, const char *keyid) -{ - if (gpg->mode != GPG_CTX_MODE_ENCRYPT) - return; - - if (!gpg->recipients) - gpg->recipients = g_ptr_array_new (); - - g_ptr_array_add (gpg->recipients, g_strdup (keyid)); -} - -static void -gpg_ctx_set_sigfile (struct _GpgCtx *gpg, const char *sigfile) -{ - g_free (gpg->sigfile); - gpg->sigfile = g_strdup (sigfile); -} - -static void -gpg_ctx_set_armor (struct _GpgCtx *gpg, gboolean armor) -{ - gpg->armor = armor; -} - -static void -gpg_ctx_set_istream (struct _GpgCtx *gpg, CamelStream *istream) -{ - camel_object_ref (CAMEL_OBJECT (istream)); - if (gpg->istream) - camel_object_unref (CAMEL_OBJECT (gpg->istream)); - gpg->istream = istream; -} - -static void -gpg_ctx_set_ostream (struct _GpgCtx *gpg, CamelStream *ostream) -{ - camel_object_ref (CAMEL_OBJECT (ostream)); - if (gpg->ostream) - camel_object_unref (CAMEL_OBJECT (gpg->ostream)); - gpg->ostream = ostream; -} - -static char * -gpg_ctx_get_diagnostics (struct _GpgCtx *gpg) -{ - return g_strndup (gpg->diagnostics->data, gpg->diagnostics->len); -} - -static void -userid_hint_free (gpointer key, gpointer value, gpointer user_data) -{ - g_free (key); - g_free (value); -} - -static void -gpg_ctx_free (struct _GpgCtx *gpg) -{ - int i; - - if (gpg->session) - camel_object_unref (CAMEL_OBJECT (gpg->session)); - - g_hash_table_foreach (gpg->userid_hint, userid_hint_free, NULL); - g_hash_table_destroy (gpg->userid_hint); - - g_free (gpg->path); - - g_free (gpg->userid); - - g_free (gpg->sigfile); - - if (gpg->recipients) { - for (i = 0; i < gpg->recipients->len; i++) - g_free (gpg->recipients->pdata[i]); - - g_ptr_array_free (gpg->recipients, TRUE); - } - - if (gpg->stdin_fd != -1) - close (gpg->stdin_fd); - if (gpg->stdout_fd != -1) - close (gpg->stdout_fd); - if (gpg->stderr_fd != -1) - close (gpg->stderr_fd); - if (gpg->status_fd != -1) - close (gpg->status_fd); - if (gpg->passwd_fd != -1) - close (gpg->passwd_fd); - - g_free (gpg->statusbuf); - - if (gpg->passwd) - g_free (gpg->passwd); - - if (gpg->istream) - camel_object_unref (CAMEL_OBJECT (gpg->istream)); - - if (gpg->ostream) - camel_object_unref (CAMEL_OBJECT (gpg->ostream)); - - g_byte_array_free (gpg->diagnostics, TRUE); - - g_free (gpg); -} - -static const char * -gpg_hash_str (CamelCipherHash hash) -{ - switch (hash) { - case CAMEL_CIPHER_HASH_MD2: - return "--digest-algo=MD2"; - case CAMEL_CIPHER_HASH_MD5: - return "--digest-algo=MD5"; - case CAMEL_CIPHER_HASH_SHA1: - return "--digest-algo=SHA1"; - case CAMEL_CIPHER_HASH_RIPEMD160: - return "--digest-algo=RIPEMD160"; - default: - return NULL; - } -} - -static GPtrArray * -gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, char **sfd, int passwd_fd, char **pfd) -{ - const char *hash_str; - GPtrArray *argv; - char *buf; - int i; - - argv = g_ptr_array_new (); - g_ptr_array_add (argv, "gpg"); - - g_ptr_array_add (argv, "--verbose"); - g_ptr_array_add (argv, "--no-secmem-warning"); - g_ptr_array_add (argv, "--no-greeting"); - g_ptr_array_add (argv, "--no-tty"); - if (passwd_fd == -1) { - /* only use batch mode if we don't intend on using the - interactive --command-fd option */ - g_ptr_array_add (argv, "--batch"); - g_ptr_array_add (argv, "--yes"); - } - - *sfd = buf = g_strdup_printf ("--status-fd=%d", status_fd); - g_ptr_array_add (argv, buf); - - if (passwd_fd != -1) { - *pfd = buf = g_strdup_printf ("--command-fd=%d", passwd_fd); - g_ptr_array_add (argv, buf); - } - - switch (gpg->mode) { - case GPG_CTX_MODE_SIGN: - g_ptr_array_add (argv, "--sign"); - g_ptr_array_add (argv, "--detach"); - if (gpg->armor) - g_ptr_array_add (argv, "--armor"); - hash_str = gpg_hash_str (gpg->hash); - if (hash_str) - g_ptr_array_add (argv, (char *) hash_str); - if (gpg->userid) { - g_ptr_array_add (argv, "-u"); - g_ptr_array_add (argv, (char *) gpg->userid); - } - g_ptr_array_add (argv, "--output"); - g_ptr_array_add (argv, "-"); - break; - case GPG_CTX_MODE_VERIFY: - if (!camel_session_is_online (gpg->session)) { - /* this is a deprecated flag to gpg since 1.0.7 */ - /*g_ptr_array_add (argv, "--no-auto-key-retrieve");*/ - g_ptr_array_add (argv, "--keyserver-options"); - g_ptr_array_add (argv, "no-auto-key-retrieve"); - } - g_ptr_array_add (argv, "--verify"); - if (gpg->sigfile) - g_ptr_array_add (argv, gpg->sigfile); - g_ptr_array_add (argv, "-"); - break; - case GPG_CTX_MODE_ENCRYPT: - g_ptr_array_add (argv, "--encrypt"); - if (gpg->armor) - g_ptr_array_add (argv, "--armor"); - if (gpg->always_trust) - g_ptr_array_add (argv, "--always-trust"); - if (gpg->userid) { - g_ptr_array_add (argv, "-u"); - g_ptr_array_add (argv, (char *) gpg->userid); - } - if (gpg->recipients) { - for (i = 0; i < gpg->recipients->len; i++) { - g_ptr_array_add (argv, "-r"); - g_ptr_array_add (argv, gpg->recipients->pdata[i]); - } - } - g_ptr_array_add (argv, "--output"); - g_ptr_array_add (argv, "-"); - break; - case GPG_CTX_MODE_DECRYPT: - g_ptr_array_add (argv, "--decrypt"); - g_ptr_array_add (argv, "--output"); - g_ptr_array_add (argv, "-"); - break; - } - - for (i = 0; i < argv->len; i++) - printf ("%s ", argv->pdata[i]); - printf ("\n"); - - g_ptr_array_add (argv, NULL); - - return argv; -} - -static int -gpg_ctx_op_start (struct _GpgCtx *gpg) -{ - char *status_fd = NULL, *passwd_fd = NULL; - int i, maxfd, errnosave, fds[10]; - GPtrArray *argv; - struct stat st; - - for (i = 0; i < 10; i++) - fds[i] = -1; - - if (stat (gpg->path, &st) == -1) - goto exception; - - maxfd = gpg->need_passwd ? 10 : 8; - for (i = 0; i < maxfd; i += 2) { - if (pipe (fds + i) == -1) - goto exception; - } - - argv = gpg_ctx_get_argv (gpg, fds[7], &status_fd, fds[8], &passwd_fd); - - if (!(gpg->pid = fork ())) { - /* child process */ - - if ((dup2 (fds[0], STDIN_FILENO) < 0 ) || - (dup2 (fds[3], STDOUT_FILENO) < 0 ) || - (dup2 (fds[5], STDERR_FILENO) < 0 )) { - _exit (255); - } - - /* Dissociate from camel's controlling terminal so - * that gpg won't be able to read from it. - */ - setsid (); - - maxfd = sysconf (_SC_OPEN_MAX); - if (maxfd > 0) { - /* Loop over all fds. */ - for (i = 0; i < maxfd; i++) { - if ((i != STDIN_FILENO) && - (i != STDOUT_FILENO) && - (i != STDERR_FILENO) && - (i != fds[7]) && /* status fd */ - (i != fds[8])) /* passwd fd */ - close (i); - } - } - - /* run gpg */ - execvp (gpg->path, (char **) argv->pdata); - _exit (255); - } else if (gpg->pid < 0) { - g_ptr_array_free (argv, TRUE); - g_free (status_fd); - g_free (passwd_fd); - goto exception; - } - - g_ptr_array_free (argv, TRUE); - g_free (status_fd); - g_free (passwd_fd); - - /* Parent */ - close (fds[0]); - gpg->stdin_fd = fds[1]; - gpg->stdout_fd = fds[2]; - close (fds[3]); - gpg->stderr_fd = fds[4]; - close (fds[5]); - gpg->status_fd = fds[6]; - close (fds[7]); - if (gpg->need_passwd) { - close (fds[8]); - gpg->passwd_fd = fds[9]; - fcntl (gpg->passwd_fd, F_SETFL, O_NONBLOCK); - } - - fcntl (gpg->stdin_fd, F_SETFL, O_NONBLOCK); - fcntl (gpg->stdout_fd, F_SETFL, O_NONBLOCK); - fcntl (gpg->stderr_fd, F_SETFL, O_NONBLOCK); - fcntl (gpg->status_fd, F_SETFL, O_NONBLOCK); - - return 0; - - exception: - - errnosave = errno; - - for (i = 0; i < 10; i++) { - if (fds[i] != -1) - close (fds[i]); - } - - errno = errnosave; - - return -1; -} - -static const char * -next_token (const char *in, char **token) -{ - const char *start, *inptr = in; - - while (*inptr == ' ') - inptr++; - - if (*inptr == '\0' || *inptr == '\n') { - if (token) - *token = NULL; - return inptr; - } - - start = inptr; - while (*inptr && *inptr != ' ' && *inptr != '\n') - inptr++; - - if (token) - *token = g_strndup (start, inptr - start); - - return inptr; -} - -static int -gpg_ctx_parse_status (struct _GpgCtx *gpg, CamelException *ex) -{ - register unsigned char *inptr; - const unsigned char *status; - int len; - - parse: - - inptr = gpg->statusbuf; - while (inptr < gpg->statusptr && *inptr != '\n') - inptr++; - - if (*inptr != '\n') { - /* we don't have enough data buffered to parse this status line */ - return 0; - } - - *inptr++ = '\0'; - status = gpg->statusbuf; - - d(printf ("status: %s\n", status)); - - if (strncmp (status, "[GNUPG:] ", 9) != 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Unexpected GnuPG status message encountered:\n\n%s"), - status); - return -1; - } - - status += 9; - - if (!strncmp (status, "USERID_HINT ", 12)) { - char *hint, *user; - - status += 12; - status = next_token (status, &hint); - if (!hint) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to parse gpg userid hint.")); - return -1; - } - - if (g_hash_table_lookup (gpg->userid_hint, hint)) { - /* we already have this userid hint... */ - g_free (hint); - goto recycle; - } - - user = g_strdup (status); - g_strstrip (user); - - g_hash_table_insert (gpg->userid_hint, hint, user); - } else if (!strncmp (status, "NEED_PASSPHRASE ", 16)) { - char *prompt, *userid, *passwd; - const char *name; - - status += 16; - - status = next_token (status, &userid); - if (!userid) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to parse gpg passphrase request.")); - return -1; - } - - name = g_hash_table_lookup (gpg->userid_hint, userid); - if (!name) - name = userid; - - prompt = g_strdup_printf (_("You need a passphrase to unlock the key for\n" - "user: \"%s\""), name); - - passwd = camel_session_get_password (gpg->session, prompt, TRUE, NULL, userid, ex); - g_free (prompt); - - g_free (gpg->userid); - gpg->userid = userid; - - if (passwd == NULL) { - if (!camel_exception_is_set (ex)) - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled.")); - return -1; - } - - gpg->passwd = g_strdup_printf ("%s\n", passwd); - memset (passwd, 0, strlen (passwd)); - g_free (passwd); - - gpg->send_passwd = TRUE; - } else if (!strncmp (status, "GOOD_PASSPHRASE", 15)) { - gpg->bad_passwds = 0; - } else if (!strncmp (status, "BAD_PASSPHRASE", 14)) { - gpg->bad_passwds++; - - camel_session_forget_password (gpg->session, NULL, gpg->userid, ex); - - if (gpg->bad_passwds == 3) { - camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Failed to unlock secret key: 3 bad passphrases given.")); - return -1; - } - } else if (!strncmp (status, "UNEXPECTED ", 11)) { - /* this is an error */ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Unexpected response from GnuPG: %s"), - status + 11); - return -1; - } else if (!strncmp (status, "NODATA", 6)) { - /* this is an error */ - if (gpg->diagnostics->len) - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "%.*s", - gpg->diagnostics->len, gpg->diagnostics->data); - else - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("No data provided")); - return -1; - } else { - /* check to see if we are complete */ - switch (gpg->mode) { - case GPG_CTX_MODE_SIGN: - if (!strncmp (status, "SIG_CREATED ", 12)) { - gpg->complete = TRUE; - } - break; - case GPG_CTX_MODE_VERIFY: - if (!strncmp (status, "TRUST_", 6)) { - status += 6; - if (!strncmp (status, "NEVER", 5)) { - gpg->trust = GPG_TRUST_NEVER; - } else if (!strncmp (status, "MARGINAL", 8)) { - gpg->trust = GPG_TRUST_MARGINAL; - } else if (!strncmp (status, "FULLY", 5)) { - gpg->trust = GPG_TRUST_FULLY; - } else if (!strncmp (status, "ULTIMATE", 8)) { - gpg->trust = GPG_TRUST_ULTIMATE; - } - - gpg->complete = TRUE; - - /* Since verifying a signature will never produce output - on gpg's stdout descriptor, we use this EOF bit for - making sure that we get a TRUST metric. */ - gpg->seen_eof1 = TRUE; - } else if (!strncmp (status, "VALIDSIG", 8)) { - gpg->validsig = TRUE; - } else if (!strncmp (status, "BADSIG", 6)) { - gpg->validsig = FALSE; - } else if (!strncmp (status, "ERRSIG", 6)) { - /* Note: NO_PUBKEY often comes after an ERRSIG, but do we really care? */ - gpg->validsig = FALSE; - } - break; - case GPG_CTX_MODE_ENCRYPT: - if (!strncmp (status, "BEGIN_ENCRYPTION", 16)) { - /* nothing to do... but we know to expect data on stdout soon */ - } else if (!strncmp (status, "END_ENCRYPTION", 14)) { - gpg->complete = TRUE; - } else if (!strncmp (status, "NO_RECP", 7)) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to encrypt: No valid recipients specified.")); - return -1; - } - break; - case GPG_CTX_MODE_DECRYPT: - if (!strncmp (status, "BEGIN_DECRYPTION", 16)) { - /* nothing to do... but we know to expect data on stdout soon */ - } else if (!strncmp (status, "END_DECRYPTION", 14)) { - gpg->complete = TRUE; - } - break; - } - - if (gpg->complete) - d(printf ("okay, that's all folks...\n")); - } - - recycle: - - /* recycle our statusbuf by moving inptr to the beginning of statusbuf */ - len = gpg->statusptr - inptr; - memmove (gpg->statusbuf, inptr, len); - - len = inptr - gpg->statusbuf; - gpg->statusleft += len; - gpg->statusptr -= len; - - /* if we have more data, try parsing the next line? */ - if (gpg->statusptr > gpg->statusbuf) - goto parse; - - return 0; -} - -#define status_backup(gpg, start, len) G_STMT_START { \ - if (gpg->statusleft <= len) { \ - unsigned int slen, soff; \ - \ - slen = soff = gpg->statusptr - gpg->statusbuf; \ - slen = slen ? slen : 1; \ - \ - while (slen < soff + len) \ - slen <<= 1; \ - \ - gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1); \ - gpg->statusptr = gpg->statusbuf + soff; \ - gpg->statusleft = slen - soff; \ - } \ - \ - memcpy (gpg->statusptr, start, len); \ - gpg->statusptr += len; \ - gpg->statusleft -= len; \ -} G_STMT_END - -static int -gpg_ctx_op_step (struct _GpgCtx *gpg, CamelException *ex) -{ - fd_set rdset, wrset, *wrsetp = NULL; - struct timeval timeout; - const char *mode; - int maxfd = 0; - int ready; - - retry: - FD_ZERO (&rdset); - FD_SET (gpg->stdout_fd, &rdset); - FD_SET (gpg->stderr_fd, &rdset); - FD_SET (gpg->status_fd, &rdset); - - maxfd = MAX (gpg->stdout_fd, gpg->stderr_fd); - maxfd = MAX (maxfd, gpg->status_fd); - - if (gpg->stdin_fd != -1 || gpg->passwd_fd != -1) { - FD_ZERO (&wrset); - if (gpg->stdin_fd != -1) { - FD_SET (gpg->stdin_fd, &wrset); - maxfd = MAX (maxfd, gpg->stdin_fd); - } - if (gpg->passwd_fd != -1) { - FD_SET (gpg->passwd_fd, &wrset); - maxfd = MAX (maxfd, gpg->passwd_fd); - } - - wrsetp = &wrset; - } - - timeout.tv_sec = 10; /* timeout in seconds */ - timeout.tv_usec = 0; - - if ((ready = select (maxfd + 1, &rdset, wrsetp, NULL, &timeout)) == 0) - return 0; - - if (ready < 0) { - if (errno == EINTR) - goto retry; - - d(printf ("select() failed: %s\n", g_strerror (errno))); - - return -1; - } - - /* Test each and every file descriptor to see if it's 'ready', - and if so - do what we can with it and then drop through to - the next file descriptor and so on until we've done what we - can to all of them. If one fails along the way, return - -1. */ - - if (FD_ISSET (gpg->status_fd, &rdset)) { - /* read the status message and decide what to do... */ - char buffer[4096]; - ssize_t nread; - - d(printf ("reading from gpg's status-fd...\n")); - - do { - nread = read (gpg->status_fd, buffer, sizeof (buffer)); - } while (nread == -1 && (errno == EINTR || errno == EAGAIN)); - if (nread == -1) - goto exception; - - if (nread > 0) { - status_backup (gpg, buffer, nread); - - if (gpg_ctx_parse_status (gpg, ex) == -1) - return -1; - } else { - gpg->complete = TRUE; - } - } - - if (FD_ISSET (gpg->stdout_fd, &rdset) && gpg->ostream) { - char buffer[4096]; - ssize_t nread; - - d(printf ("reading gpg's stdout...\n")); - - do { - nread = read (gpg->stdout_fd, buffer, sizeof (buffer)); - } while (nread == -1 && (errno == EINTR || errno == EAGAIN)); - if (nread == -1) - goto exception; - - if (nread > 0) { - if (camel_stream_write (gpg->ostream, buffer, (size_t) nread) == -1) - goto exception; - } else { - gpg->seen_eof1 = TRUE; - } - } - - if (FD_ISSET (gpg->stderr_fd, &rdset)) { - char buffer[4096]; - ssize_t nread; - - d(printf ("reading gpg's stderr...\n")); - - do { - nread = read (gpg->stderr_fd, buffer, sizeof (buffer)); - } while (nread == -1 && (errno == EINTR || errno == EAGAIN)); - if (nread == -1) - goto exception; - - if (nread > 0) { - g_byte_array_append (gpg->diagnostics, buffer, nread); - } else { - gpg->seen_eof2 = TRUE; - } - } - - if (wrsetp && gpg->passwd_fd != -1 && FD_ISSET (gpg->passwd_fd, &wrset) && gpg->need_passwd && gpg->send_passwd) { - ssize_t w, nwritten = 0; - size_t n; - - d(printf ("sending gpg our passphrase...\n")); - - /* send the passphrase to gpg */ - n = strlen (gpg->passwd); - do { - do { - w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten); - } while (w == -1 && (errno == EINTR || errno == EAGAIN)); - - if (w > 0) - nwritten += w; - } while (nwritten < n && w != -1); - - /* zero and free our passwd buffer */ - memset (gpg->passwd, 0, n); - g_free (gpg->passwd); - gpg->passwd = NULL; - - if (w == -1) - goto exception; - - gpg->send_passwd = FALSE; - } - - if (wrsetp && gpg->stdin_fd != -1 && FD_ISSET (gpg->stdin_fd, &wrset)) { - char buffer[4096]; - ssize_t nread; - - d(printf ("writing to gpg's stdin...\n")); - - /* write our stream to gpg's stdin */ - nread = camel_stream_read (gpg->istream, buffer, sizeof (buffer)); - if (nread > 0) { - ssize_t w, nwritten = 0; - - do { - do { - w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten); - } while (w == -1 && (errno == EINTR || errno == EAGAIN)); - - if (w > 0) - nwritten += w; - } while (nwritten < nread && w != -1); - - d(printf ("wrote %d (out of %d) bytes to gpg's stdin\n", nwritten, nread)); - - if (w == -1) - goto exception; - } - - if (camel_stream_eos (gpg->istream)) { - d(printf ("closing gpg's stdin\n")); - close (gpg->stdin_fd); - gpg->stdin_fd = -1; - } - } - - return 0; - - exception: - - switch (gpg->mode) { - case GPG_CTX_MODE_SIGN: - mode = "sign"; - break; - case GPG_CTX_MODE_VERIFY: - mode = "verify"; - break; - case GPG_CTX_MODE_ENCRYPT: - mode = "encrypt"; - break; - case GPG_CTX_MODE_DECRYPT: - mode = "decrypt"; - break; - default: - g_assert_not_reached (); - mode = NULL; - break; - } - - if (gpg->diagnostics->len) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to GPG %s message: %s\n\n%.*s"), - mode, g_strerror (errno), - gpg->diagnostics->len, - gpg->diagnostics->data); - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to GPG %s message: %s\n"), - mode, g_strerror (errno)); - } - - return -1; -} - -static gboolean -gpg_ctx_op_complete (struct _GpgCtx *gpg) -{ - return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2; -} - -static gboolean -gpg_ctx_op_exited (struct _GpgCtx *gpg) -{ - pid_t retval; - int status; - - if (gpg->exited) - return TRUE; - - retval = waitpid (gpg->pid, &status, WNOHANG); - if (retval == gpg->pid) { - gpg->exit_status = status; - gpg->exited = TRUE; - return TRUE; - } - - return FALSE; -} - -static void -gpg_ctx_op_cancel (struct _GpgCtx *gpg) -{ - pid_t retval; - int status; - - if (gpg->exited) - return; - - kill (gpg->pid, SIGTERM); - sleep (1); - retval = waitpid (gpg->pid, &status, WNOHANG); - if (retval == (pid_t) 0) { - /* no more mr nice guy... */ - kill (gpg->pid, SIGKILL); - sleep (1); - waitpid (gpg->pid, &status, WNOHANG); - } -} - -static int -gpg_ctx_op_wait (struct _GpgCtx *gpg) -{ - sigset_t mask, omask; - pid_t retval; - int status; - - if (!gpg->exited) { - sigemptyset (&mask); - sigaddset (&mask, SIGALRM); - sigprocmask (SIG_BLOCK, &mask, &omask); - alarm (1); - retval = waitpid (gpg->pid, &status, 0); - alarm (0); - sigprocmask (SIG_SETMASK, &omask, NULL); - - if (retval == (pid_t) -1 && errno == EINTR) { - /* The child is hanging: send a friendly reminder. */ - kill (gpg->pid, SIGTERM); - sleep (1); - retval = waitpid (gpg->pid, &status, WNOHANG); - if (retval == (pid_t) 0) { - /* Still hanging; use brute force. */ - kill (gpg->pid, SIGKILL); - sleep (1); - retval = waitpid (gpg->pid, &status, WNOHANG); - } - } - } else { - status = gpg->exit_status; - retval = gpg->pid; - } - - if (retval != (pid_t) -1 && WIFEXITED (status)) - return WEXITSTATUS (status); - else - return -1; -} - - - -static int -gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, - CamelStream *istream, CamelStream *ostream, CamelException *ex) -{ - CamelGpgContext *ctx = (CamelGpgContext *) context; - struct _GpgCtx *gpg; - - gpg = gpg_ctx_new (context->session, ctx->path); - gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN); - gpg_ctx_set_hash (gpg, hash); - gpg_ctx_set_armor (gpg, TRUE); - gpg_ctx_set_userid (gpg, userid); - gpg_ctx_set_istream (gpg, istream); - gpg_ctx_set_ostream (gpg, ostream); - - if (gpg_ctx_op_start (gpg) == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to execute gpg: %s"), g_strerror (errno)); - gpg_ctx_free (gpg); - - return -1; - } - - while (!gpg_ctx_op_complete (gpg)) { - if (camel_operation_cancel_check (NULL)) { - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, - _("Cancelled.")); - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - - if (gpg_ctx_op_step (gpg, ex) == -1) { - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - } - - if (gpg_ctx_op_wait (gpg) != 0) { - char *diagnostics; - - diagnostics = gpg_ctx_get_diagnostics (gpg); - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - diagnostics && *diagnostics ? diagnostics : - _("Failed to execute gpg.")); - g_free (diagnostics); - - gpg_ctx_free (gpg); - - return -1; - } - - gpg_ctx_free (gpg); - - return 0; -} - - -static char * -swrite (CamelStream *istream) -{ - CamelStream *ostream; - char *template; - int fd, ret; - - template = g_strdup ("/tmp/evolution-pgp.XXXXXX"); - fd = mkstemp (template); - if (fd == -1) { - g_free (template); - return NULL; - } - - ostream = camel_stream_fs_new_with_fd (fd); - ret = camel_stream_write_to_stream (istream, ostream); - if (ret != -1) { - ret = camel_stream_flush (ostream); - if (ret != -1) - ret = camel_stream_close (ostream); - } - camel_object_unref (CAMEL_OBJECT (ostream)); - - if (ret == -1) { - unlink (template); - g_free (template); - return NULL; - } - - return template; -} - -static CamelCipherValidity * -gpg_verify (CamelCipherContext *context, CamelCipherHash hash, - CamelStream *istream, CamelStream *sigstream, - CamelException *ex) -{ - CamelGpgContext *ctx = (CamelGpgContext *) context; - CamelCipherValidity *validity; - char *diagnostics = NULL; - struct _GpgCtx *gpg; - char *sigfile = NULL; - gboolean valid; - - if (sigstream != NULL) { - /* We are going to verify a detached signature so save - the signature to a temp file. */ - sigfile = swrite (sigstream); - if (sigfile == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot verify message signature: " - "could not create temp file: %s"), - g_strerror (errno)); - return NULL; - } - } - - gpg = gpg_ctx_new (context->session, ctx->path); - gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY); - gpg_ctx_set_hash (gpg, hash); - gpg_ctx_set_sigfile (gpg, sigfile); - gpg_ctx_set_istream (gpg, istream); - - if (gpg_ctx_op_start (gpg) == -1) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to execute gpg.")); - gpg_ctx_free (gpg); - goto exception; - } - - while (!gpg_ctx_op_complete (gpg) && !gpg_ctx_op_exited (gpg)) { - if (camel_operation_cancel_check (NULL)) { - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, - _("Cancelled.")); - gpg_ctx_op_cancel (gpg); - goto exception; - } - - if (gpg_ctx_op_step (gpg, ex) == -1) { - gpg_ctx_op_cancel (gpg); - goto exception; - } - } - - diagnostics = gpg_ctx_get_diagnostics (gpg); - - valid = gpg_ctx_op_wait (gpg) == 0; - gpg_ctx_free (gpg); - - validity = camel_cipher_validity_new (); - camel_cipher_validity_set_valid (validity, valid); - camel_cipher_validity_set_description (validity, diagnostics); - g_free (diagnostics); - - if (sigfile) { - unlink (sigfile); - g_free (sigfile); - } - - return validity; - - exception: - - diagnostics = gpg_ctx_get_diagnostics (gpg); - - gpg_ctx_free (gpg); - - if (sigfile) { - unlink (sigfile); - g_free (sigfile); - } - - return NULL; -} - - -static int -gpg_encrypt (CamelCipherContext *context, gboolean sign, const char *userid, - GPtrArray *recipients, CamelStream *istream, CamelStream *ostream, - CamelException *ex) -{ - CamelGpgContext *ctx = (CamelGpgContext *) context; - struct _GpgCtx *gpg; - int i; - - gpg = gpg_ctx_new (context->session, ctx->path); - gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT); - gpg_ctx_set_armor (gpg, TRUE); - gpg_ctx_set_userid (gpg, userid); - gpg_ctx_set_istream (gpg, istream); - gpg_ctx_set_ostream (gpg, ostream); - gpg_ctx_set_always_trust (gpg, ctx->always_trust); - - for (i = 0; i < recipients->len; i++) { - gpg_ctx_add_recipient (gpg, recipients->pdata[i]); - } - - if (gpg_ctx_op_start (gpg) == -1) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to execute gpg.")); - gpg_ctx_free (gpg); - - return -1; - } - - while (!gpg_ctx_op_complete (gpg)) { - if (camel_operation_cancel_check (NULL)) { - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, - _("Cancelled.")); - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - - if (gpg_ctx_op_step (gpg, ex) == -1) { - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - } - - if (gpg_ctx_op_wait (gpg) != 0) { - char *diagnostics; - - diagnostics = gpg_ctx_get_diagnostics (gpg); - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - diagnostics && *diagnostics ? diagnostics : - _("Failed to execute gpg.")); - g_free (diagnostics); - - gpg_ctx_free (gpg); - - return -1; - } - - gpg_ctx_free (gpg); - - return 0; -} - - -static int -gpg_decrypt (CamelCipherContext *context, CamelStream *istream, - CamelStream *ostream, CamelException *ex) -{ - CamelGpgContext *ctx = (CamelGpgContext *) context; - struct _GpgCtx *gpg; - - gpg = gpg_ctx_new (context->session, ctx->path); - gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT); - gpg_ctx_set_istream (gpg, istream); - gpg_ctx_set_ostream (gpg, ostream); - - if (gpg_ctx_op_start (gpg) == -1) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to execute gpg.")); - gpg_ctx_free (gpg); - - return -1; - } - - while (!gpg_ctx_op_complete (gpg)) { - if (camel_operation_cancel_check (NULL)) { - camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, - _("Cancelled.")); - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - - if (gpg_ctx_op_step (gpg, ex) == -1) { - gpg_ctx_op_cancel (gpg); - gpg_ctx_free (gpg); - - return -1; - } - } - - if (gpg_ctx_op_wait (gpg) != 0) { - char *diagnostics; - - diagnostics = gpg_ctx_get_diagnostics (gpg); - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - diagnostics && *diagnostics ? diagnostics : - _("Failed to execute gpg.")); - g_free (diagnostics); - - gpg_ctx_free (gpg); - - return -1; - } - - gpg_ctx_free (gpg); - - return 0; -} |