/* elmo - ELectronic Mail Operator Copyright (C) 2004 rzyjontko 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; version 2. 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 Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef GPG_SUPPORT # ifdef HAVE_GPGME4 # include # else # include # endif #endif #include #include #include "pgp.h" #include "read.h" #include "gettext.h" #include "ask.h" #include "mime.h" #include "xmalloc.h" #include "file.h" #include "mlex.h" #include "rmime.h" #include "hash.h" #include "error.h" #include "gettext.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define PREAMBLE do { if (! initialized) return; } while (0) /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct pass_list { struct pass_list *next; char *hint; char *pass; }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ #ifdef GPG_SUPPORT /* GPGME context */ static gpgme_ctx_t ctx; #endif #ifdef GPG_SUPPORT /* How should we hide the password. */ static hide_mode_t hide = HIDE_ASTERISK; #endif static int initialized = 0; /* List of passwords read by user. */ static struct pass_list *passwords = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void destroy_passwords (struct pass_list *list) { if (list == NULL) return; destroy_passwords (list->next); xfree (list->hint); xfree (list->pass); xfree (list); } #ifdef GPG_SUPPORT static void add_password (const char *hint, const char *pass) { struct pass_list *list = xmalloc (sizeof (struct pass_list)); list->hint = xstrdup (hint); list->pass = xstrdup (pass); list->next = passwords; passwords = list; } static char * find_password (struct pass_list *list, const char *hint) { if (list == NULL) return NULL; if (strcmp (list->hint, hint) == 0) return list->pass; return find_password (list->next, hint); } static gpgme_error_t passphrase_cb (void *hook, const char *hint, const char *info, int prev_bad, int fd) { char *answer = NULL; if (! prev_bad){ answer = find_password (passwords, hint); } if (answer == NULL){ answer = read_argument (_("Password: "), NULL, COMPLETE_NONE, hide); } if (answer == NULL){ write (fd, "\n", 1); return GPG_ERR_CANCELED; } write (fd, answer, strlen (answer)); write (fd, "\n", 1); add_password (hint, answer); return GPG_ERR_NO_ERROR; } static gpgme_data_t data_from_mime (const char *fname, mime_t *mime) { gpgme_data_t result; gpgme_data_new_from_filepart (& result, fname, NULL, mime->off_start, mime->off_end - mime->off_start); return result; } static gpgme_data_t data_from_mime_with_header (const char *fname, mime_t *mime) { gpgme_data_t result; gpgme_data_new_from_filepart (& result, fname, NULL, mime->off_header, mime->off_end - mime->off_header); return result; } static gpgme_data_t data_from_tmp_file (FILE **fp, char **fname) { gpgme_data_t result; *fp = file_temp_file (fname); gpgme_data_new_from_stream (& result, *fp); return result; } static mime_t * parse_file (FILE *fp) { mime_t *mime; rewind (fp); yyin = fp; if (mlex_scan_file (0) != NEXT_MAIL) return NULL; mime = newmail->mime->mime; newmail->mime->mime = NULL; mail_destroy (newmail, BOX_INVALID); return mime; } static void decrypt (mime_info_t *mime_info) { gpgme_data_t in, out; FILE *fp; char *fname; in = data_from_mime (mime_info->file, mime_info->mime->parts->array[1]); out = data_from_tmp_file (& fp, & fname); if (gpgme_op_decrypt (ctx, in, out) == GPG_ERR_NO_ERROR){ mime_info->decrypted = parse_file (fp); mime_info->d_file = fname; } else { xfree (fname); destroy_passwords (passwords); } fclose (fp); gpgme_data_release (in); gpgme_data_release (out); } static char * sig_from_fpr (const char *fpr) { gpgme_key_t key; str_t *str; char *name; if (gpgme_get_key (ctx, fpr, & key, 0) != GPG_ERR_NO_ERROR) return NULL; name = mime_decode_utf8 (key->uids->name); str = str_create (); str_sprintf (str, "%s <%s>", name, key->uids->email); xfree (name); gpgme_key_unref (key); return str_finished (str); } static void get_sig_info (mime_info_t *mime) { gpgme_verify_result_t result = gpgme_op_verify_result (ctx); gpgme_error_t error = result->signatures->status; gpgme_sigsum_t summary = result->signatures->summary; mime->sig_by = sig_from_fpr (result->signatures->fpr); if (summary & GPGME_SIGSUM_VALID){ mime->sig = SIG_OK; } else if (summary & GPGME_SIGSUM_GREEN){ mime->sig = SIG_WARN; mime->sig_text = xstrdup (gpgme_strerror (error)); } else if (summary == 0){ mime->sig = SIG_WARN; mime->sig_text = xstrdup (gpgme_strerror (error)); } else { mime->sig = SIG_FAIL; mime->sig_text = xstrdup (gpgme_strerror (error)); } } static void verify (mime_info_t *mime_info) { gpgme_data_t mail, sig; gpgme_error_t err; if (mime_info->sig != SIG_NOT_SIGNED) return; sig = data_from_mime (mime_info->file, mime_info->mime->parts->array[1]); mail = data_from_mime_with_header (mime_info->file, mime_info->mime->parts->array[0]); err = gpgme_op_verify (ctx, sig, mail, NULL); if (err == GPG_ERR_NO_ERROR){ get_sig_info (mime_info); } else { error_ (0, _("couldn't verify signature: %s"), gpgme_strerror (err)); } gpgme_data_release (sig); gpgme_data_release (mail); } #else static void decrypt (mime_info_t *mime) { } static void verify (mime_info_t *mime) { } #endif /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void pgp_init (void) { #if defined (GPG_SUPPORT) && defined (HAVE_LOCALE_H) char *shide; gpgme_check_version (NULL); gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); shide = ask_for_default ("hide_password", NULL); if (shide && strcasecmp (shide, "yes") == 0) hide = HIDE_EMPTY; if (gpgme_new (& ctx) == GPG_ERR_NO_ERROR){ gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); gpgme_set_armor (ctx, 1); gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL); initialized = 1; } #endif } void pgp_free_resources (void) { PREAMBLE; #ifdef GPG_SUPPORT gpgme_release (ctx); destroy_passwords (passwords); #endif initialized = 0; passwords = NULL; } void pgp_decrypt_verify (mime_info_t *mime_info) { PREAMBLE; if (strcasecmp (mime_info->mime->type, "multipart/encrypted") == 0){ decrypt (mime_info); } else if (strcasecmp (mime_info->mime->type, "multipart/signed") == 0){ verify (mime_info); } } void pgp_forget_passphrase (void) { destroy_passwords (passwords); passwords = NULL; error_ (0, "%s", _("passwords have been forgotten")); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE pgp.c * ****************************************************************************/