/* * Copyright (C) 2001-2002 Oliver Ehli * Copyright (C) 2002 Mike Schiraldi * Copyright (C) 2004 g10 Code GmbH * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "smime.h" #include "mime.h" #include "copy.h" #include "send.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_RESOURCE_H # include #endif #ifdef CRYPT_BACKEND_CLASSIC_SMIME #include "mutt_crypt.h" struct smime_command_context { const char *key; /* %k */ const char *cryptalg; /* %a */ const char *digestalg; /* %d */ const char *fname; /* %f */ const char *sig_fname; /* %s */ const char *certificates; /* %c */ const char *intermediates; /* %i */ }; char SmimePass[STRING]; time_t SmimeExptime = 0; /* when does the cached passphrase expire? */ static BUFFER *SmimeKeyToUse = NULL; static BUFFER *SmimeCertToUse = NULL; static BUFFER *SmimeIntermediateToUse = NULL; void smime_init (void) { SmimeKeyToUse = mutt_buffer_new (); SmimeCertToUse = mutt_buffer_new (); SmimeIntermediateToUse = mutt_buffer_new (); } void smime_cleanup (void) { mutt_buffer_free (&SmimeKeyToUse); mutt_buffer_free (&SmimeCertToUse); mutt_buffer_free (&SmimeIntermediateToUse); } void smime_free_key (smime_key_t **keylist) { smime_key_t *key; if (!keylist) return; while (*keylist) { key = *keylist; *keylist = (*keylist)->next; FREE (&key->email); FREE (&key->hash); FREE (&key->label); FREE (&key->issuer); FREE (&key); } } static smime_key_t *smime_copy_key (smime_key_t *key) { smime_key_t *copy; if (!key) return NULL; copy = safe_calloc (sizeof (smime_key_t), 1); copy->email = safe_strdup(key->email); copy->hash = safe_strdup(key->hash); copy->label = safe_strdup(key->label); copy->issuer = safe_strdup(key->issuer); copy->trust = key->trust; copy->flags = key->flags; return copy; } /* * Queries and passphrase handling. */ /* these are copies from pgp.c */ void smime_void_passphrase (void) { memset (SmimePass, 0, sizeof (SmimePass)); SmimeExptime = 0; } int smime_valid_passphrase (void) { time_t now = time (NULL); if (now < SmimeExptime) /* Use cached copy. */ return 1; smime_void_passphrase(); if (mutt_get_password (_("Enter S/MIME passphrase:"), SmimePass, sizeof (SmimePass)) == 0) { SmimeExptime = mutt_add_timeout (time (NULL), SmimeTimeout); return (1); } else SmimeExptime = 0; return 0; } /* * The OpenSSL interface */ /* This is almost identical to ppgp's invoking interface. */ static const char *_mutt_fmt_smime_command (char *dest, size_t destlen, size_t col, int cols, char op, const char *src, const char *prefix, const char *ifstring, const char *elsestring, void *data, format_flag flags) { char fmt[16]; struct smime_command_context *cctx = (struct smime_command_context *) data; int optional = (flags & MUTT_FORMAT_OPTIONAL); switch (op) { case 'C': { if (!optional) { BUFFER *path, *buf1, *buf2; struct stat sb; path = mutt_buffer_pool_get (); buf1 = mutt_buffer_pool_get (); buf2 = mutt_buffer_pool_get (); mutt_buffer_strcpy (path, NONULL (SmimeCALocation)); mutt_buffer_expand_path (path); mutt_buffer_quote_filename (buf1, mutt_b2s (path)); if (stat (mutt_b2s (path), &sb) != 0 || !S_ISDIR (sb.st_mode)) mutt_buffer_printf (buf2, "-CAfile %s", mutt_b2s (buf1)); else mutt_buffer_printf (buf2, "-CApath %s", mutt_b2s (buf1)); snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, mutt_b2s (buf2)); mutt_buffer_pool_release (&path); mutt_buffer_pool_release (&buf1); mutt_buffer_pool_release (&buf2); } else if (!SmimeCALocation) optional = 0; break; } case 'c': { /* certificate (list) */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL(cctx->certificates)); } else if (!cctx->certificates) optional = 0; break; } case 'i': { /* intermediate certificates */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL(cctx->intermediates)); } else if (!cctx->intermediates) optional = 0; break; } case 's': { /* detached signature */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname)); } else if (!cctx->sig_fname) optional = 0; break; } case 'k': { /* private key */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->key)); } else if (!cctx->key) optional = 0; break; } case 'a': { /* algorithm for encryption */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg)); } else if (!cctx->key) optional = 0; break; } case 'f': { /* file to process */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->fname)); } else if (!cctx->fname) optional = 0; break; } case 'd': { /* algorithm for the signature message digest */ if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->digestalg)); } else if (!cctx->key) optional = 0; break; } default: *dest = '\0'; break; } if (optional) mutt_FormatString (dest, destlen, col, cols, ifstring, _mutt_fmt_smime_command, data, 0); else if (flags & MUTT_FORMAT_OPTIONAL) mutt_FormatString (dest, destlen, col, cols, elsestring, _mutt_fmt_smime_command, data, 0); return (src); } static void mutt_smime_command (char *d, size_t dlen, struct smime_command_context *cctx, const char *fmt) { mutt_FormatString (d, dlen, 0, MuttIndexWindow->cols, NONULL(fmt), _mutt_fmt_smime_command, cctx, 0); dprint (2,(debugfile, "mutt_smime_command: %s\n", d)); } static pid_t smime_invoke (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *sig_fname, const char *cryptalg, const char *digestalg, const char *key, const char *certificates, const char *intermediates, const char *format) { struct smime_command_context cctx; char cmd[HUGE_STRING]; memset (&cctx, 0, sizeof (cctx)); if (!format || !*format) return (pid_t) -1; cctx.fname = fname; cctx.sig_fname = sig_fname; cctx.key = key; cctx.cryptalg = cryptalg; cctx.digestalg = digestalg; cctx.certificates = certificates; cctx.intermediates = intermediates; mutt_smime_command (cmd, sizeof (cmd), &cctx, format); return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd); } /* * Key and certificate handling. */ static char *smime_key_flags (int flags) { static char buff[3]; if (!(flags & KEYFLAG_CANENCRYPT)) buff[0] = '-'; else buff[0] = 'e'; if (!(flags & KEYFLAG_CANSIGN)) buff[1] = '-'; else buff[1] = 's'; buff[2] = '\0'; return buff; } static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num) { smime_key_t **Table = (smime_key_t **) menu->data; smime_key_t *this = Table[num]; char* truststate; switch (this->trust) { case 't': truststate = N_("Trusted "); break; case 'v': truststate = N_("Verified "); break; case 'u': truststate = N_("Unverified"); break; case 'e': truststate = N_("Expired "); break; case 'r': truststate = N_("Revoked "); break; case 'i': truststate = N_("Invalid "); break; default: truststate = N_("Unknown "); } snprintf(s, l, " 0x%s %s %s %-35.35s %s", this->hash, smime_key_flags (this->flags), truststate, this->email, this->label); } static smime_key_t *smime_select_key (smime_key_t *keys, char *query) { smime_key_t **table = NULL; int table_size = 0; int table_index = 0; smime_key_t *key = NULL; smime_key_t *selected_key = NULL; char helpstr[LONG_STRING]; char buf[LONG_STRING]; char title[256]; MUTTMENU* menu; char *s = ""; int done = 0; for (table_index = 0, key = keys; key; key = key->next) { if (table_index == table_size) { table_size += 5; safe_realloc (&table, sizeof (smime_key_t *) * table_size); } table[table_index++] = key; } snprintf(title, sizeof(title), _("S/MIME certificates matching \"%s\"."), query); /* Make Helpstring */ helpstr[0] = 0; mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME, OP_GENERIC_SELECT_ENTRY); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof(buf), _("Help"), MENU_SMIME, OP_HELP); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ /* Create the menu */ menu = mutt_new_menu(MENU_SMIME); menu->max = table_index; menu->make_entry = smime_entry; menu->help = helpstr; menu->data = table; menu->title = title; mutt_push_current_menu (menu); /* sorting keys might be done later - TODO */ mutt_clear_error(); done = 0; while (!done) { switch (mutt_menuLoop (menu)) { case OP_GENERIC_SELECT_ENTRY: if (table[menu->current]->trust != 't') { switch (table[menu->current]->trust) { case 'i': case 'r': case 'e': s = N_("ID is expired/disabled/revoked."); break; case 'u': s = N_("ID has undefined validity."); break; case 'v': s = N_("ID is not trusted."); break; } snprintf (buf, sizeof (buf), _("%s Do you really want to use the key?"), _(s)); if (mutt_yesorno (buf, MUTT_NO) != MUTT_YES) { mutt_clear_error (); break; } } selected_key = table[menu->current]; done = 1; break; case OP_EXIT: done = 1; break; } } mutt_pop_current_menu (menu); mutt_menuDestroy (&menu); FREE (&table); return selected_key; } static smime_key_t *smime_parse_key(char *buf) { smime_key_t *key; char *pend, *p; int field = 0; key = safe_calloc (sizeof (smime_key_t), 1); for (p = buf; p; p = pend) { /* Some users manually maintain their .index file, and use a tab * as a delimiter, which the old parsing code (using fscanf) * happened to allow. smime_keys.pl uses a space, so search for both. */ if ((pend = strchr (p, ' ')) || (pend = strchr (p, '\t')) || (pend = strchr (p, '\n'))) *pend++ = 0; /* For backward compatibility, don't count consecutive delimiters * as an empty field. */ if (!*p) continue; field++; switch (field) { case 1: /* mailbox */ key->email = safe_strdup (p); break; case 2: /* hash */ key->hash = safe_strdup (p); break; case 3: /* label */ key->label = safe_strdup (p); break; case 4: /* issuer */ key->issuer = safe_strdup (p); break; case 5: /* trust */ key->trust = *p; break; case 6: /* purpose */ while (*p) { switch (*p++) { case 'e': key->flags |= KEYFLAG_CANENCRYPT; break; case 's': key->flags |= KEYFLAG_CANSIGN; break; } } break; } } /* Old index files could be missing issuer, trust, and purpose, * but anything less than that is an error. */ if (field < 3) { smime_free_key (&key); return NULL; } if (field < 4) key->issuer = safe_strdup ("?"); if (field < 5) key->trust = 't'; if (field < 6) key->flags = (KEYFLAG_CANENCRYPT | KEYFLAG_CANSIGN); return key; } static smime_key_t *smime_get_candidates(char *search, short public) { BUFFER *index_file; FILE *fp; char buf[LONG_STRING]; smime_key_t *key, *results, **results_end; results = NULL; results_end = &results; index_file = mutt_buffer_pool_get (); mutt_buffer_printf (index_file, "%s/.index", public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys)); if ((fp = safe_fopen (mutt_b2s (index_file), "r")) == NULL) { mutt_perror (mutt_b2s (index_file)); mutt_buffer_pool_release (&index_file); return NULL; } mutt_buffer_pool_release (&index_file); while (fgets (buf, sizeof (buf), fp)) { if ((! *search) || mutt_stristr (buf, search)) { key = smime_parse_key (buf); if (key) { *results_end = key; results_end = &key->next; } } } safe_fclose (&fp); return results; } /* Returns the first matching key record, without prompting or checking of * abilities or trust. */ static smime_key_t *smime_get_key_by_hash(char *hash, short public) { smime_key_t *results, *result; smime_key_t *match = NULL; results = smime_get_candidates(hash, public); for (result = results; result; result = result->next) { if (mutt_strcasecmp (hash, result->hash) == 0) { match = smime_copy_key (result); break; } } smime_free_key (&results); return match; } static smime_key_t *smime_get_key_by_addr(char *mailbox, short abilities, short public, short oppenc_mode) { smime_key_t *results, *result; smime_key_t *matches = NULL; smime_key_t **matches_end = &matches; smime_key_t *match; smime_key_t *trusted_match = NULL; smime_key_t *valid_match = NULL; smime_key_t *return_key = NULL; int multi_trusted_matches = 0; if (! mailbox) return NULL; results = smime_get_candidates(mailbox, public); for (result = results; result; result = result->next) { if (abilities && !(result->flags & abilities)) { continue; } if (mutt_strcasecmp (mailbox, result->email) == 0) { match = smime_copy_key (result); *matches_end = match; matches_end = &match->next; if (match->trust == 't') { if (trusted_match && (mutt_strcasecmp (match->hash, trusted_match->hash) != 0)) { multi_trusted_matches = 1; } trusted_match = match; } else if ((match->trust == 'u') || (match->trust == 'v')) { valid_match = match; } } } smime_free_key (&results); if (matches) { if (oppenc_mode) { if (trusted_match) return_key = smime_copy_key (trusted_match); else if (valid_match && !option (OPTCRYPTOPPENCSTRONGKEYS)) return_key = smime_copy_key (valid_match); else return_key = NULL; } else if (trusted_match && !multi_trusted_matches) { return_key = smime_copy_key (trusted_match); } else { return_key = smime_copy_key (smime_select_key (matches, mailbox)); } smime_free_key (&matches); } return return_key; } static smime_key_t *smime_get_key_by_str(char *str, short abilities, short public) { smime_key_t *results, *result; smime_key_t *matches = NULL; smime_key_t **matches_end = &matches; smime_key_t *match; smime_key_t *return_key = NULL; if (! str) return NULL; results = smime_get_candidates(str, public); for (result = results; result; result = result->next) { if (abilities && !(result->flags & abilities)) { continue; } if ((mutt_strcasecmp (str, result->hash) == 0) || mutt_stristr(result->email, str) || mutt_stristr(result->label, str)) { match = smime_copy_key (result); *matches_end = match; matches_end = &match->next; } } smime_free_key (&results); if (matches) { return_key = smime_copy_key (smime_select_key (matches, str)); smime_free_key (&matches); } return return_key; } smime_key_t *smime_ask_for_key(char *prompt, short abilities, short public) { smime_key_t *key; char resp[SHORT_STRING]; if (!prompt) prompt = _("Enter keyID: "); mutt_clear_error (); FOREVER { resp[0] = 0; if (mutt_get_field (prompt, resp, sizeof (resp), MUTT_CLEAR) != 0) return NULL; if ((key = smime_get_key_by_str (resp, abilities, public))) return key; BEEP (); } } /* This sets the '*ToUse' variables for an upcoming decryption, where the required key is different from SmimeDefaultKey. */ void _smime_getkeys (char *mailbox) { smime_key_t *key = NULL; const char *k = NULL; char buf[STRING]; size_t smime_keys_len; smime_keys_len = mutt_strlen (SmimeKeys); key = smime_get_key_by_addr (mailbox, KEYFLAG_CANENCRYPT, 0, 0); if (!key) { snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), mailbox); key = smime_ask_for_key (buf, KEYFLAG_CANENCRYPT, 0); } k = key ? key->hash : NONULL (SmimeDefaultKey); /* if the key is different from last time */ if ((mutt_buffer_len (SmimeKeyToUse) <= smime_keys_len) || mutt_strcasecmp (k, SmimeKeyToUse->data + smime_keys_len + 1)) { smime_void_passphrase (); mutt_buffer_printf (SmimeKeyToUse, "%s/%s", NONULL(SmimeKeys), k); mutt_buffer_printf (SmimeCertToUse, "%s/%s", NONULL(SmimeCertificates), k); } smime_free_key (&key); } void smime_getkeys (ENVELOPE *env) { ADDRESS *t; int found = 0; if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey) { mutt_buffer_printf (SmimeKeyToUse, "%s/%s", NONULL (SmimeKeys), SmimeDefaultKey); mutt_buffer_printf (SmimeCertToUse, "%s/%s", NONULL(SmimeCertificates), SmimeDefaultKey); return; } for (t = env->to; !found && t; t = t->next) if (mutt_addr_is_user (t)) { found = 1; _smime_getkeys (t->mailbox); } for (t = env->cc; !found && t; t = t->next) if (mutt_addr_is_user (t)) { found = 1; _smime_getkeys (t->mailbox); } if (!found && (t = mutt_default_from())) { _smime_getkeys (t->mailbox); rfc822_free_address (&t); } } /* This routine attempts to find the keyids of the recipients of a message. * It returns NULL if any of the keys can not be found. * If oppenc_mode is true, only keys that can be determined without * prompting will be used. */ char *smime_findKeys (ADDRESS *adrlist, int oppenc_mode) { smime_key_t *key = NULL; char *keyID, *keylist = NULL; size_t keylist_size = 0; size_t keylist_used = 0; ADDRESS *p, *q; for (p = adrlist; p ; p = p->next) { char buf[LONG_STRING]; q = p; key = smime_get_key_by_addr (q->mailbox, KEYFLAG_CANENCRYPT, 1, oppenc_mode); if ((key == NULL) && (! oppenc_mode)) { snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), q->mailbox); key = smime_ask_for_key (buf, KEYFLAG_CANENCRYPT, 1); } if (!key) { if (! oppenc_mode) mutt_message (_("No (valid) certificate found for %s."), q->mailbox); FREE (&keylist); return NULL; } keyID = key->hash; keylist_size += mutt_strlen (keyID) + 2; safe_realloc (&keylist, keylist_size); sprintf (keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyID); /* __SPRINTF_CHECKED__ */ keylist_used = mutt_strlen (keylist); smime_free_key (&key); } return (keylist); } static int smime_handle_cert_email (char *certificate, char *mailbox, int copy, char ***buffer, int *num) { FILE *fpout = NULL, *fperr = NULL; BUFFER *tmpfname; char email[STRING]; int ret = -1, count = 0; pid_t thepid; size_t len = 0; tmpfname = mutt_buffer_pool_get (); mutt_buffer_mktemp (tmpfname); if ((fperr = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); return 1; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_mktemp (tmpfname); if ((fpout = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { safe_fclose (&fperr); mutt_perror (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); return 1; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fpout), fileno (fperr), certificate, NULL, NULL, NULL, NULL, NULL, NULL, SmimeGetCertEmailCommand))== -1) { mutt_message (_("Error: unable to create OpenSSL subprocess!")); safe_fclose (&fperr); safe_fclose (&fpout); return 1; } mutt_wait_filter (thepid); fflush (fpout); rewind (fpout); fflush (fperr); rewind (fperr); while ((fgets (email, sizeof (email), fpout))) { len = mutt_strlen (email); if (len && (email[len - 1] == '\n')) email[len - 1] = '\0'; if (mutt_strncasecmp (email, mailbox, mutt_strlen (mailbox)) == 0) ret=1; ret = ret < 0 ? 0 : ret; count++; } if (ret == -1) { mutt_endwin(NULL); mutt_copy_stream (fperr, stdout); mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); ret = 1; } else if (!ret) ret = 1; else ret = 0; if (copy && buffer && num) { (*num) = count; *buffer = safe_calloc(sizeof(char*), count); count = 0; rewind (fpout); while ((fgets (email, sizeof (email), fpout))) { len = mutt_strlen (email); if (len && (email[len - 1] == '\n')) email[len - 1] = '\0'; (*buffer)[count] = safe_calloc(1, mutt_strlen (email) + 1); strncpy((*buffer)[count], email, mutt_strlen (email)); count++; } } else if (copy) ret = 2; safe_fclose (&fpout); safe_fclose (&fperr); return ret; } static char *smime_extract_certificate (const char *infile) { FILE *fperr = NULL, *fppk7out = NULL, *fpcertfile = NULL; BUFFER *tmpfname = NULL, *pk7out = NULL, *certfile = NULL; char *retval = NULL; pid_t thepid; int empty; tmpfname = mutt_buffer_pool_get (); pk7out = mutt_buffer_pool_get (); certfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (tmpfname); if ((fperr = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); goto cleanup; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_mktemp (pk7out); if ((fppk7out = safe_fopen (mutt_b2s (pk7out), "w+")) == NULL) { mutt_perror (mutt_b2s (pk7out)); goto cleanup; } /* Step 1: Convert the signature to a PKCS#7 structure, as we can't extract the full set of certificates directly. */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fppk7out), fileno (fperr), infile, NULL, NULL, NULL, NULL, NULL, NULL, SmimePk7outCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); goto cleanup; } mutt_wait_filter (thepid); fflush (fppk7out); rewind (fppk7out); fflush (fperr); rewind (fperr); empty = (fgetc (fppk7out) == EOF); if (empty) { mutt_perror (mutt_b2s (pk7out)); mutt_copy_stream (fperr, stdout); goto cleanup; } safe_fclose (&fppk7out); mutt_buffer_mktemp (certfile); if ((fpcertfile = safe_fopen (mutt_b2s (certfile), "w+")) == NULL) { mutt_perror (mutt_b2s (certfile)); mutt_unlink (mutt_b2s (pk7out)); goto cleanup; } /* Step 2: Extract the certificates from a PKCS#7 structure. */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fpcertfile), fileno (fperr), mutt_b2s (pk7out), NULL, NULL, NULL, NULL, NULL, NULL, SmimeGetCertCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); mutt_unlink (mutt_b2s (pk7out)); goto cleanup; } mutt_wait_filter (thepid); mutt_unlink (mutt_b2s (pk7out)); fflush (fpcertfile); rewind (fpcertfile); fflush (fperr); rewind (fperr); empty = (fgetc (fpcertfile) == EOF); if (empty) { mutt_copy_stream (fperr, stdout); goto cleanup; } safe_fclose (&fpcertfile); retval = safe_strdup (mutt_b2s (certfile)); cleanup: safe_fclose (&fperr); if (fppk7out) { safe_fclose (&fppk7out); mutt_unlink (mutt_b2s (pk7out)); } if (fpcertfile) { safe_fclose (&fpcertfile); mutt_unlink (mutt_b2s (certfile)); } mutt_buffer_pool_release (&tmpfname); mutt_buffer_pool_release (&pk7out); mutt_buffer_pool_release (&certfile); return retval; } static char *smime_extract_signer_certificate (const char *infile) { FILE *fpout = NULL, *fperr = NULL; char *retval = NULL; BUFFER *tmpfname = NULL, *certfile = NULL; pid_t thepid; int empty; tmpfname = mutt_buffer_pool_get (); mutt_buffer_mktemp (tmpfname); if ((fperr = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); goto cleanup; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); certfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (certfile); if ((fpout = safe_fopen (mutt_b2s (certfile), "w+")) == NULL) { mutt_perror (mutt_b2s (certfile)); goto cleanup; } /* Extract signer's certificate */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, -1, fileno (fperr), infile, NULL, NULL, NULL, NULL, mutt_b2s (certfile), NULL, SmimeGetSignerCertCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); goto cleanup; } mutt_wait_filter (thepid); fflush (fpout); rewind (fpout); fflush (fperr); rewind (fperr); empty = (fgetc (fpout) == EOF); if (empty) { mutt_endwin (NULL); mutt_copy_stream (fperr, stdout); mutt_any_key_to_continue (NULL); goto cleanup; } safe_fclose (&fpout); retval = safe_strdup (mutt_b2s (certfile)); cleanup: safe_fclose (&fperr); if (fpout) { safe_fclose (&fpout); mutt_unlink (mutt_b2s (certfile)); } mutt_buffer_pool_release (&tmpfname); mutt_buffer_pool_release (&certfile); return retval; } /* Add a certificate and update index file (externally). */ void smime_invoke_import (const char *infile, const char *mailbox) { BUFFER *tmpfname; char *certfile = NULL, buf[STRING]; FILE *smimein=NULL, *fpout = NULL, *fperr = NULL; pid_t thepid=-1; tmpfname = mutt_buffer_pool_get (); mutt_buffer_mktemp (tmpfname); if ((fperr = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); return; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_mktemp (tmpfname); if ((fpout = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); safe_fclose (&fperr); mutt_buffer_pool_release (&tmpfname); return; } mutt_unlink (mutt_b2s (tmpfname)); mutt_buffer_pool_release (&tmpfname); buf[0] = '\0'; if (option (OPTASKCERTLABEL)) mutt_get_field (_("Label for certificate: "), buf, sizeof (buf), 0); mutt_endwin (NULL); if ((certfile = smime_extract_certificate(infile))) { mutt_endwin (NULL); if ((thepid = smime_invoke (&smimein, NULL, NULL, -1, fileno(fpout), fileno(fperr), certfile, NULL, NULL, NULL, NULL, NULL, NULL, SmimeImportCertCommand))== -1) { mutt_message (_("Error: unable to create OpenSSL subprocess!")); return; } fputs (buf, smimein); fputc ('\n', smimein); safe_fclose (&smimein); mutt_wait_filter (thepid); mutt_unlink (certfile); FREE (&certfile); } fflush (fpout); rewind (fpout); fflush (fperr); rewind (fperr); mutt_copy_stream (fpout, stdout); mutt_copy_stream (fperr, stdout); safe_fclose (&fpout); safe_fclose (&fperr); } int smime_verify_sender(HEADER *h) { char *mbox = NULL, *certfile; BUFFER *tempfname; FILE *fpout; int retval=1; tempfname = mutt_buffer_pool_get (); mutt_buffer_mktemp (tempfname); if (!(fpout = safe_fopen (mutt_b2s (tempfname), "w"))) { mutt_perror (mutt_b2s (tempfname)); goto cleanup; } if (h->security & ENCRYPT) mutt_copy_message (fpout, Context, h, MUTT_CM_DECODE_CRYPT & MUTT_CM_DECODE_SMIME, CH_MIME|CH_WEED|CH_NONEWLINE); else mutt_copy_message (fpout, Context, h, 0, 0); fflush(fpout); safe_fclose (&fpout); if (h->env->from) { h->env->from = mutt_expand_aliases (h->env->from); mbox = h->env->from->mailbox; } else if (h->env->sender) { h->env->sender = mutt_expand_aliases (h->env->sender); mbox = h->env->sender->mailbox; } if (mbox) { if ((certfile = smime_extract_signer_certificate (mutt_b2s (tempfname)))) { mutt_unlink(mutt_b2s (tempfname)); if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) { if (isendwin()) mutt_any_key_to_continue(NULL); } else retval = 0; mutt_unlink(certfile); FREE (&certfile); } else mutt_any_key_to_continue(_("no certfile")); } else mutt_any_key_to_continue(_("no mbox")); mutt_unlink(mutt_b2s (tempfname)); cleanup: mutt_buffer_pool_release (&tempfname); return retval; } /* * Creating S/MIME - bodies. */ static pid_t smime_invoke_encrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *uids) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd, fname, NULL, SmimeCryptAlg, NULL, NULL, uids, NULL, SmimeEncryptCommand); } static pid_t smime_invoke_sign (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd, fname, NULL, NULL, SmimeDigestAlg, mutt_b2s (SmimeKeyToUse), mutt_b2s (SmimeCertToUse), mutt_b2s (SmimeIntermediateToUse), SmimeSignCommand); } BODY *smime_build_smime_entity (BODY *a, char *certlist) { char buf[LONG_STRING], certfile[LONG_STRING]; BUFFER *tempfile = NULL, *smimeerrfile = NULL, *smimeinfile = NULL; char *cert_start, *cert_end; FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL; BODY *t = NULL; int err = 0, empty; size_t off; pid_t thepid; tempfile = mutt_buffer_pool_get (); smimeerrfile = mutt_buffer_pool_get (); smimeinfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (tempfile); if ((fpout = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL) { mutt_perror (mutt_b2s (tempfile)); goto cleanup; } mutt_buffer_mktemp (smimeerrfile); if ((smimeerr = safe_fopen (mutt_b2s (smimeerrfile), "w+")) == NULL) { mutt_perror (mutt_b2s (smimeerrfile)); goto cleanup; } mutt_unlink (mutt_b2s (smimeerrfile)); mutt_buffer_mktemp (smimeinfile); if ((fptmp = safe_fopen (mutt_b2s (smimeinfile), "w+")) == NULL) { mutt_perror (mutt_b2s (smimeinfile)); goto cleanup; } *certfile = '\0'; for (cert_start = certlist; cert_start; cert_start = cert_end) { if ((cert_end = strchr (cert_start, ' '))) *cert_end = '\0'; if (*cert_start) { off = mutt_strlen (certfile); snprintf (certfile+off, sizeof (certfile)-off, "%s%s/%s", off ? " " : "", NONULL(SmimeCertificates), cert_start); } if (cert_end) *cert_end++ = ' '; } /* write a MIME entity */ mutt_write_mime_header (a, fptmp); fputc ('\n', fptmp); mutt_write_mime_body (a, fptmp); safe_fclose (&fptmp); if ((thepid = smime_invoke_encrypt (&smimein, NULL, NULL, -1, fileno (fpout), fileno (smimeerr), mutt_b2s (smimeinfile), certfile)) == -1) { mutt_unlink (mutt_b2s (smimeinfile)); goto cleanup; } safe_fclose (&smimein); mutt_wait_filter (thepid); mutt_unlink (mutt_b2s (smimeinfile)); fflush (fpout); rewind (fpout); empty = (fgetc (fpout) == EOF); safe_fclose (&fpout); fflush (smimeerr); rewind (smimeerr); while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) { err = 1; fputs (buf, stdout); } safe_fclose (&smimeerr); /* pause if there is any error output from SMIME */ if (err) mutt_any_key_to_continue (NULL); if (empty) { /* fatal error while trying to encrypt message */ if (!err) mutt_any_key_to_continue _("No output from OpenSSL..."); mutt_unlink (mutt_b2s (tempfile)); goto cleanup; } t = mutt_new_body (); t->type = TYPEAPPLICATION; t->subtype = safe_strdup ("x-pkcs7-mime"); mutt_set_parameter ("name", "smime.p7m", &t->parameter); mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter); t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */ t->use_disp = 1; t->disposition = DISPATTACH; t->d_filename = safe_strdup ("smime.p7m"); t->filename = safe_strdup (mutt_b2s (tempfile)); t->unlink = 1; /*delete after sending the message */ t->parts=0; t->next=0; cleanup: if (fpout) { safe_fclose (&fpout); mutt_unlink (mutt_b2s (tempfile)); } safe_fclose (&smimeerr); if (fptmp) { safe_fclose (&fptmp); mutt_unlink (mutt_b2s (smimeinfile)); } mutt_buffer_pool_release (&tempfile); mutt_buffer_pool_release (&smimeerrfile); mutt_buffer_pool_release (&smimeinfile); return (t); } /* The openssl -md doesn't want hyphens: * md5, sha1, sha224, sha256, sha384, sha512 * However, the micalg does: * md5, sha-1, sha-224, sha-256, sha-384, sha-512 */ static char *openssl_md_to_smime_micalg(char *md) { char *micalg; size_t l; if (!md) return 0; if (mutt_strncasecmp ("sha", md, 3) == 0) { l = strlen (md) + 2; micalg = (char *)safe_malloc (l); snprintf (micalg, l, "sha-%s", md +3); } else { micalg = safe_strdup (md); } return micalg; } BODY *smime_sign_message (BODY *a ) { BODY *t, *retval = NULL; char buffer[LONG_STRING]; BUFFER *filetosign = NULL, *signedfile = NULL; FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL; int err = 0; int empty = 0; pid_t thepid; char *signas; smime_key_t *signas_key; char *intermediates; char *micalg; signas = SmimeSignAs ? SmimeSignAs : SmimeDefaultKey; if (!signas) { mutt_error _("Can't sign: No key specified. Use Sign As."); return NULL; } convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */ filetosign = mutt_buffer_pool_get (); signedfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (filetosign); if ((sfp = safe_fopen (mutt_b2s (filetosign), "w+")) == NULL) { mutt_perror (mutt_b2s (filetosign)); goto cleanup; } mutt_buffer_mktemp (signedfile); if ((smimeout = safe_fopen (mutt_b2s (signedfile), "w+")) == NULL) { mutt_perror (mutt_b2s (signedfile)); goto cleanup; } mutt_write_mime_header (a, sfp); fputc ('\n', sfp); mutt_write_mime_body (a, sfp); safe_fclose (&sfp); mutt_buffer_printf (SmimeKeyToUse, "%s/%s", NONULL(SmimeKeys), signas); mutt_buffer_printf (SmimeCertToUse, "%s/%s", NONULL(SmimeCertificates), signas); signas_key = smime_get_key_by_hash (signas, 1); if ((! signas_key) || (! mutt_strcmp ("?", signas_key->issuer))) intermediates = signas; /* so openssl won't complain in any case */ else intermediates = signas_key->issuer; mutt_buffer_printf (SmimeIntermediateToUse, "%s/%s", NONULL(SmimeCertificates), intermediates); smime_free_key (&signas_key); if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr, -1, fileno (smimeout), -1, mutt_b2s (filetosign))) == -1) { mutt_perror _("Can't open OpenSSL subprocess!"); mutt_unlink (mutt_b2s (filetosign)); goto cleanup; } fputs (SmimePass, smimein); fputc ('\n', smimein); safe_fclose (&smimein); mutt_wait_filter (thepid); /* check for errors from OpenSSL */ err = 0; fflush (smimeerr); rewind (smimeerr); while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) { err = 1; fputs (buffer, stdout); } safe_fclose (&smimeerr); fflush (smimeout); rewind (smimeout); empty = (fgetc (smimeout) == EOF); safe_fclose (&smimeout); mutt_unlink (mutt_b2s (filetosign)); if (err) mutt_any_key_to_continue (NULL); if (empty) { mutt_any_key_to_continue _("No output from OpenSSL..."); mutt_unlink (mutt_b2s (signedfile)); goto cleanup; } t = mutt_new_body (); t->type = TYPEMULTIPART; t->subtype = safe_strdup ("signed"); t->encoding = ENC7BIT; t->use_disp = 0; t->disposition = DISPINLINE; mutt_generate_boundary (&t->parameter); micalg = openssl_md_to_smime_micalg (SmimeDigestAlg); mutt_set_parameter ("micalg", micalg, &t->parameter); FREE (&micalg); mutt_set_parameter ("protocol", "application/x-pkcs7-signature", &t->parameter); t->parts = a; retval = t; t->parts->next = mutt_new_body (); t = t->parts->next; t->type = TYPEAPPLICATION; t->subtype = safe_strdup ("x-pkcs7-signature"); t->filename = safe_strdup (mutt_b2s (signedfile)); t->d_filename = safe_strdup ("smime.p7s"); t->use_disp = 1; t->disposition = DISPATTACH; t->encoding = ENCBASE64; t->unlink = 1; /* ok to remove this file after sending. */ cleanup: if (sfp) { safe_fclose (&sfp); mutt_unlink (mutt_b2s (filetosign)); } if (smimeout) { safe_fclose (&smimeout); mutt_unlink (mutt_b2s (signedfile)); } mutt_buffer_pool_release (&filetosign); mutt_buffer_pool_release (&signedfile); return (retval); } /* * Handling S/MIME - bodies. */ static pid_t smime_invoke_verify (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *sig_fname, int opaque) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL, (opaque ? SmimeVerifyOpaqueCommand : SmimeVerifyCommand)); } static pid_t smime_invoke_decrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd, fname, NULL, NULL, NULL, mutt_b2s (SmimeKeyToUse), mutt_b2s (SmimeCertToUse), NULL, SmimeDecryptCommand); } int smime_verify_one (BODY *sigbdy, STATE *s, const char *tempfile) { BUFFER *signedfile = NULL, *smimeerrfile = NULL; FILE *smimeout=NULL, *smimeerr=NULL; pid_t thepid; int badsig = -1; FILE *s_fpout_save; char *s_prefix_save; signedfile = mutt_buffer_pool_get (); smimeerrfile = mutt_buffer_pool_get (); mutt_buffer_printf (signedfile, "%s.sig", tempfile); /* decode to a tempfile, saving the original destination */ s_fpout_save = s->fpout; if ((s->fpout = safe_fopen (mutt_b2s (signedfile), "w")) == NULL) { mutt_perror (mutt_b2s (signedfile)); s->fpout = s_fpout_save; goto signedfile_cleanup; } /* if we are decoding binary bodies, we don't want to prefix each * line with the prefix or else the data will get corrupted. */ s_prefix_save = s->prefix; s->prefix = NULL; mutt_decode_attachment (sigbdy, s); s->prefix = s_prefix_save; safe_fclose (&s->fpout); s->fpout = s_fpout_save; mutt_buffer_mktemp (smimeerrfile); if (!(smimeerr = safe_fopen (mutt_b2s (smimeerrfile), "w+"))) { mutt_perror (mutt_b2s (smimeerrfile)); goto errfile_cleanup; } crypt_current_time (s, "OpenSSL"); if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL, -1, -1, fileno (smimeerr), tempfile, mutt_b2s (signedfile), 0)) != -1) { fflush (smimeout); safe_fclose (&smimeout); if (mutt_wait_filter (thepid)) badsig = -1; else { char *line = NULL; int lineno = 0; size_t linelen; fflush (smimeerr); rewind (smimeerr); line = mutt_read_line (line, &linelen, smimeerr, &lineno, 0); if (linelen && !ascii_strcasecmp (line, "verification successful")) badsig = 0; FREE (&line); } } fflush (smimeerr); rewind (smimeerr); mutt_copy_stream (smimeerr, s->fpout); safe_fclose (&smimeerr); state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s); mutt_unlink (mutt_b2s (smimeerrfile)); errfile_cleanup: mutt_unlink (mutt_b2s (signedfile)); signedfile_cleanup: mutt_buffer_pool_release (&signedfile); mutt_buffer_pool_release (&smimeerrfile); return badsig; } /* This handles application/pkcs7-mime which can either be a signed or an encrypted message. */ static BODY *smime_handle_entity (BODY *m, STATE *s, FILE *outFile) { size_t len=0; int c; char buf[HUGE_STRING]; BUFFER *outfile = NULL, *errfile = NULL, *tmpfname = NULL; BUFFER *tmptmpfname = NULL; FILE *smimeout = NULL, *smimein=NULL, *smimeerr=NULL; FILE *tmpfp=NULL, *tmpfp_buffer=NULL, *fpout=NULL; struct stat info; BODY *p=NULL; pid_t thepid=-1; unsigned int type = mutt_is_application_smime (m); if (!(type & APPLICATION_SMIME)) return NULL; /* Because of the mutt_body_handler() we avoid the buffer pool. */ outfile = mutt_buffer_new (); errfile = mutt_buffer_new (); tmpfname = mutt_buffer_new (); mutt_buffer_mktemp (outfile); if ((smimeout = safe_fopen (mutt_b2s (outfile), "w+")) == NULL) { mutt_perror (mutt_b2s (outfile)); goto cleanup; } mutt_buffer_mktemp (errfile); if ((smimeerr = safe_fopen (mutt_b2s (errfile), "w+")) == NULL) { mutt_perror (mutt_b2s (errfile)); goto cleanup; } mutt_unlink (mutt_b2s (errfile)); mutt_buffer_mktemp (tmpfname); if ((tmpfp = safe_fopen (mutt_b2s (tmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmpfname)); goto cleanup; } fseeko (s->fpin, m->offset, SEEK_SET); mutt_copy_bytes (s->fpin, tmpfp, m->length); fflush (tmpfp); safe_fclose (&tmpfp); if ((type & ENCRYPT) && (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1, fileno (smimeout), fileno (smimeerr), mutt_b2s (tmpfname))) == -1) { mutt_unlink (mutt_b2s (tmpfname)); if (s->flags & MUTT_DISPLAY) state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s); goto cleanup; } else if ((type & SIGNOPAQUE) && (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1, fileno (smimeout), fileno (smimeerr), NULL, mutt_b2s (tmpfname), SIGNOPAQUE)) == -1) { mutt_unlink (mutt_b2s (tmpfname)); if (s->flags & MUTT_DISPLAY) state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s); goto cleanup; } if (type & ENCRYPT) { if (!smime_valid_passphrase ()) smime_void_passphrase (); fputs (SmimePass, smimein); fputc ('\n', smimein); } safe_fclose (&smimein); mutt_wait_filter (thepid); mutt_unlink (mutt_b2s (tmpfname)); if (s->flags & MUTT_DISPLAY) { fflush (smimeerr); rewind (smimeerr); if ((c = fgetc (smimeerr)) != EOF) { ungetc (c, smimeerr); crypt_current_time (s, "OpenSSL"); mutt_copy_stream (smimeerr, s->fpout); state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s); } if (type & ENCRYPT) state_attach_puts (_("[-- The following data is S/MIME" " encrypted --]\n"), s); else state_attach_puts (_("[-- The following data is S/MIME signed --]\n"), s); } fflush (smimeout); rewind (smimeout); if (type & ENCRYPT) { /* void the passphrase, even if that wasn't the problem */ if (fgetc (smimeout) == EOF) { mutt_error _("Decryption failed"); smime_void_passphrase (); } rewind (smimeout); } if (outFile) fpout = outFile; else { tmptmpfname = mutt_buffer_new (); mutt_buffer_mktemp (tmptmpfname); if ((fpout = safe_fopen (mutt_b2s (tmptmpfname), "w+")) == NULL) { mutt_perror (mutt_b2s (tmptmpfname)); goto cleanup; } } while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) { len = mutt_strlen (buf); if (len > 1 && buf[len - 2] == '\r') { buf[len-2] = '\n'; buf[len-1] = '\0'; } fputs (buf, fpout); } fflush (fpout); rewind (fpout); if ((p = mutt_read_mime_header (fpout, 0)) != NULL) { fstat (fileno (fpout), &info); p->length = info.st_size - p->offset; mutt_parse_part (fpout, p); if (s->flags & MUTT_DISPLAY) { mutt_protected_headers_handler (p, s); } /* Store any protected headers in the parent so they can be * accessed for index updates after the handler recursion is done. * This is done before the handler to prevent a nested encrypted * handler from freeing the headers. */ mutt_free_envelope (&m->mime_headers); m->mime_headers = p->mime_headers; p->mime_headers = NULL; if (s->fpout) { rewind (fpout); tmpfp_buffer = s->fpin; s->fpin = fpout; mutt_body_handler (p, s); s->fpin = tmpfp_buffer; } /* Embedded multipart signed protected headers override the * encrypted headers. We need to do this after the handler so * they can be printed in the pager. */ if (!(type & SMIMESIGN) && mutt_is_multipart_signed (p) && p->parts && p->parts->mime_headers) { mutt_free_envelope (&m->mime_headers); m->mime_headers = p->parts->mime_headers; p->parts->mime_headers = NULL; } } safe_fclose (&smimeout); mutt_unlink (mutt_b2s (outfile)); if (!outFile) { safe_fclose (&fpout); mutt_unlink (mutt_b2s (tmptmpfname)); } fpout = NULL; if (s->flags & MUTT_DISPLAY) { if (type & ENCRYPT) state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s); else state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s); } if (type & SIGNOPAQUE) { char *line = NULL; int lineno = 0; size_t linelen; rewind (smimeerr); line = mutt_read_line (line, &linelen, smimeerr, &lineno, 0); if (linelen && !ascii_strcasecmp (line, "verification successful")) m->goodsig = 1; FREE (&line); } else if (p) { m->goodsig = p->goodsig; m->badsig = p->badsig; } safe_fclose (&smimeerr); cleanup: if (smimeout) { safe_fclose (&smimeout); mutt_unlink (mutt_b2s (outfile)); } safe_fclose (&smimeerr); safe_fclose (&tmpfp); if (!outFile && fpout) { safe_fclose (&fpout); mutt_unlink (mutt_b2s (tmptmpfname)); } mutt_buffer_free (&outfile); mutt_buffer_free (&errfile); mutt_buffer_free (&tmpfname); mutt_buffer_free (&tmptmpfname); return (p); } int smime_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur) { BUFFER *tempfile = NULL; STATE s; LOFF_T tmpoffset = b->offset; size_t tmplength = b->length; FILE *tmpfp=NULL; int rv = -1; if (!mutt_is_application_smime (b)) return -1; if (b->parts) return -1; *fpout = NULL; memset (&s, 0, sizeof (s)); s.fpin = fpin; fseeko (s.fpin, b->offset, SEEK_SET); tempfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (tempfile); if ((tmpfp = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL) { mutt_perror (mutt_b2s (tempfile)); goto bail; } mutt_unlink (mutt_b2s (tempfile)); s.fpout = tmpfp; mutt_decode_attachment (b, &s); fflush (tmpfp); b->length = ftello (s.fpout); b->offset = 0; rewind (tmpfp); s.fpin = tmpfp; s.fpout = 0; mutt_buffer_mktemp (tempfile); if ((*fpout = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL) { mutt_perror (mutt_b2s (tempfile)); goto bail; } mutt_unlink (mutt_b2s (tempfile)); mutt_buffer_pool_release (&tempfile); if (!(*cur = smime_handle_entity (b, &s, *fpout))) { goto bail; } rv = 0; (*cur)->goodsig = b->goodsig; (*cur)->badsig = b->badsig; bail: b->length = tmplength; b->offset = tmpoffset; safe_fclose (&tmpfp); if (*fpout) rewind (*fpout); mutt_buffer_pool_release (&tempfile); return rv; } int smime_application_smime_handler (BODY *m, STATE *s) { int rv = 1; BODY *tattach; /* clear out any mime headers before the handler, so they can't be * spoofed. */ mutt_free_envelope (&m->mime_headers); tattach = smime_handle_entity (m, s, NULL); if (tattach) { rv = 0; mutt_free_body (&tattach); } return rv; } void smime_send_menu (SEND_CONTEXT *sctx) { HEADER *msg; smime_key_t *key; char *prompt, *letters, *choices; int choice; msg = sctx->msg; if (!(WithCrypto & APPLICATION_SMIME)) return; msg->security |= APPLICATION_SMIME; /* * Opportunistic encrypt is controlling encryption. * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different * letter choices for those. */ if (option (OPTCRYPTOPPORTUNISTICENCRYPT) && (msg->security & OPPENCRYPT)) { prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off? "); /* L10N: The 'f' is from "forget it", an old undocumented synonym of 'clear'. Please use a corresponding letter in your language. Alternatively, you may duplicate the letter 'c' is translated to. This comment also applies to the two following letter sequences. */ letters = _("swafco"); choices = "SwaFCo"; } /* * Opportunistic encryption option is set, but is toggled off * for this message. */ else if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode? "); letters = _("eswabfco"); choices = "eswabfcO"; } /* * Opportunistic encryption is unset */ else { prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "); letters = _("eswabfc"); choices = "eswabfc"; } choice = mutt_multi_choice (prompt, letters); if (choice > 0) { switch (choices[choice - 1]) { case 'e': /* (e)ncrypt */ msg->security |= ENCRYPT; msg->security &= ~SIGN; break; case 'w': /* encrypt (w)ith */ { msg->security |= ENCRYPT; do { /* I use "dra" because "123" is recognized anyway */ switch (mutt_multi_choice (_("Choose algorithm family:" " 1: DES, 2: RC2, 3: AES," " or (c)lear? "), _("drac"))) { case 1: switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "), _("dt"))) { case 1: mutt_str_replace (&sctx->smime_crypt_alg, "des"); break; case 2: mutt_str_replace (&sctx->smime_crypt_alg, "des3"); break; } break; case 2: switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "), _("468"))) { case 1: mutt_str_replace (&sctx->smime_crypt_alg, "rc2-40"); break; case 2: mutt_str_replace (&sctx->smime_crypt_alg, "rc2-64"); break; case 3: mutt_str_replace (&sctx->smime_crypt_alg, "rc2-128"); break; } break; case 3: switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "), _("895"))) { case 1: mutt_str_replace (&sctx->smime_crypt_alg, "aes128"); break; case 2: mutt_str_replace (&sctx->smime_crypt_alg, "aes192"); break; case 3: mutt_str_replace (&sctx->smime_crypt_alg, "aes256"); break; } break; case 4: /* (c)lear */ FREE (&sctx->smime_crypt_alg); sctx->smime_crypt_alg_cleared = 1; /* fall through */ case -1: /* Ctrl-G or Enter */ choice = 0; break; } } while (choice == -1); } break; case 's': /* (s)ign */ msg->security &= ~ENCRYPT; msg->security |= SIGN; break; case 'S': /* (s)ign in oppenc mode */ msg->security |= SIGN; break; case 'a': /* sign (a)s */ if ((key = smime_ask_for_key (_("Sign as: "), KEYFLAG_CANSIGN, 0))) { mutt_str_replace (&sctx->smime_sign_as, key->hash); smime_free_key (&key); msg->security |= SIGN; /* probably need a different passphrase */ crypt_smime_void_passphrase (); } break; case 'b': /* (b)oth */ msg->security |= (ENCRYPT | SIGN); break; case 'f': /* (f)orget it: kept for backward compatibility. */ case 'c': /* (c)lear */ msg->security &= ~(ENCRYPT | SIGN); break; case 'F': /* (f)orget it or (c)lear in oppenc mode */ case 'C': msg->security &= ~SIGN; break; case 'O': /* oppenc mode on */ msg->security |= OPPENCRYPT; crypt_opportunistic_encrypt (msg); break; case 'o': /* oppenc mode off */ msg->security &= ~OPPENCRYPT; break; } } } #endif /* CRYPT_BACKEND_CLASSIC_SMIME */