···11+OP_DECRYPT_SAVE "make decrypted copy and delete"
22+OP_DECRYPT_COPY "make decrypted copy"
33+OP_FORGET_PASSPHRASE "wipe passphrase(s) from memory"
44+OP_EXTRACT_KEYS "extract supported public keys"
-4
OPS.PGP
···11OP_COMPOSE_ATTACH_KEY "attach a PGP public key"
22OP_COMPOSE_PGP_MENU "show PGP options"
33-OP_EXTRACT_KEYS "extract PGP public keys"
44-OP_FORGET_PASSPHRASE "wipe PGP passphrase from memory"
53OP_MAIL_KEY "mail a PGP public key"
64OP_VERIFY_KEY "verify a PGP public key"
75OP_VIEW_ID "view the key's user id"
88-OP_DECRYPT_SAVE "make decrypted copy and delete"
99-OP_DECRYPT_COPY "make decrypted copy"
106OP_CHECK_TRADITIONAL "check for classic pgp"
···7979 OPS="$OPS \$(srcdir)/OPS.PGP"
8080 fi
81818282+ AC_ARG_ENABLE(smime, [ --disable-smime Disable SMIME support],
8383+ [ if test x$enableval = xno ; then
8484+ have_smime=no
8585+ fi
8686+ ])
8787+8888+ if test x$have_smime != xno ; then
8989+ AC_DEFINE(HAVE_SMIME,1,[ Define if you want S/MIME support. ])
9090+ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS smime.o "
9191+ OPS="$OPS \$(srcdir)/OPS.SMIME"
9292+ SMIMEAUX_TARGET="smime_keys"
9393+ fi
9494+9595+ if test x$HAVE_SMIME != xno -o x$HAVE_PGP != xno ; then
9696+ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS crypt.o "
9797+ OPS="$OPS \$(srcdir)/OPS.CRYPT"
9898+ fi
9999+82100 AC_ARG_WITH(mixmaster, [ --with-mixmaster[=PATH] Include Mixmaster support],
83101 [if test -x "$withval" ; then
84102 MIXMASTER="$withval"
···95113AC_SUBST(OPS)
9611497115AC_SUBST(PGPAUX_TARGET)
116116+AC_SUBST(SMIMEAUX_TARGET)
9811799118AC_DEFINE_UNQUOTED(SUBVERSION,"$SUBVERSION",[ Is this the international version? ])
100119AC_SUBST(SUBVERSION)
···11+# -*-muttrc-*-
22+## The following options are only available if you have
33+## compiled in S/MIME support
44+55+set smime_is_default
66+77+# Passphrase expiration
88+set smime_timeout=300
99+1010+# Global crypto options -- these affect PGP operations as well.
1111+set crypt_autosign = yes
1212+set crypt_replyencrypt = yes
1313+set crypt_replysign = yes
1414+set crypt_replysignencrypted = yes
1515+set crypt_verify_sig = yes
1616+1717+# Section A: Key Management.
1818+1919+# The (default) keyfile for signing/decrypting. Uncomment the following
2020+# line and replace the keyid with your own.
2121+#set smime_sign_as="12345678.0"
2222+2323+# Path to where all known certificates go. (must exist!)
2424+set smime_certificates="~/.smime/certificates"
2525+2626+# Path to where all private keys go. (must exist!)
2727+set smime_keys="~/.smime/keys"
2828+2929+# These are used to extract a certificate from a message.
3030+# First generate a PKCS#7 structure from the message.
3131+set smime_pk7out_command="openssl smime -verify -in %f -noverify -pk7out"
3232+3333+# Extract the included certificate(s) from a PKCS#7 structure.
3434+set smime_get_cert_command="openssl pkcs7 -print_certs -in %f"
3535+3636+# Extract the signer's certificate only from a S/MIME signature (sender verification)
3737+set smime_get_signer_cert_command="openssl smime -verify -in %f -noverify -signer %c -out /dev/null"
3838+3939+# This is used to get a filename for certificates that get stored in the
4040+# above directory
4141+set smime_hash_cert_command="openssl x509 -in %f -noout -hash"
4242+4343+# This is used to get the email address the certificate was issued to.
4444+set smime_get_cert_email_command="openssl x509 -in %f -noout -email"
4545+4646+4747+4848+# Sction B: Outgoing messages
4949+5050+# Algorithm to use for encryption.
5151+# valid choices are rc2-40, rc2-64, rc2-128, des, des3
5252+set smime_encrypt_with="des3"
5353+5454+# Encrypt a message. Input file is a MIME entity.
5555+set smime_encrypt_command="openssl smime -encrypt -%a -outform DER -in %f %c"
5656+5757+# Sign.
5858+set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER"
5959+6060+6161+6262+#Section C: Incoming messages
6363+6464+# Decrypt a message. Output is a MIME entity.
6565+set smime_decrypt_command="openssl smime -decrypt -passin stdin -inform DER -in %f -inkey %k -recip %c"
6666+6767+# Verify a signature of type multipart/signed
6868+set smime_verify_command="openssl smime -verify -inform DER -in %s -CAfile ~/.smime/ca-bundle.crt -content %f"
6969+7070+# Verify a signature of type application/x-pkcs7-mime
7171+set smime_verify_opaque_command="openssl smime -verify -inform DER -in %s -CAfile ~/.smime/ca-bundle.crt"
7272+7373+7474+7575+# Section D: Alternatives
7676+7777+# Sign. If you wish to NOT include the certificate your CA used in signing
7878+# your public key, use this command instead.
7979+# set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -outform DER"
8080+#
8181+# In order to verify the signature only and skip checking the certificate chain:
8282+#
8383+# set smime_verify_command="openssl smime -verify -inform DER -in %s -content %f -noverify"
8484+# set smime_verify_opaque_command="openssl smime -verify -inform DER -in %s -noverify"
8585+#
8686+# setup with the directory flag:
8787+#
8888+# Verify a signature of type multipart/signed
8989+# set smime_verify_command="openssl smime -verify -inform DER -in %s -CApath ~/.smime/root_certs -content %f"
9090+#
9191+# Verify a signature of type application/x-pkcs7-mime
9292+# set smime_verify_opaque_command="openssl smime -verify -inform DER -in %s -CApath ~/.smime/root_certs"
···29293030#ifdef HAVE_PGP
3131#define M_CM_DECODE_PGP (1<<8) /* used for decoding PGP messages */
3232-#define M_CM_VERIFY (1<<9) /* do signature verification */
3232+#define M_CM_DECODE_CRYPT (1<<8)
3333+#endif
3434+3535+3636+#ifdef HAVE_SMIME
3737+#ifdef M_CM_DECODE_CRYPT
3838+#undef M_CM_DECODE_CRYPT
3339#endif
4040+#define M_CM_DECODE_SMIME (1<<9) /* used for decoding S/MIME messages */
4141+#ifdef HAVE_PGP
4242+#undef M_CM_DECODE_CRYPT
4343+#define M_CM_DECODE_CRYPT (M_CM_DECODE_PGP | M_CM_DECODE_SMIME)
4444+#else
4545+#define M_CM_DECODE_CRYPT (1<<9) /* used for decoding S/MIME messages */
4646+#endif
4747+#endif
4848+4949+5050+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
5151+#define M_CM_VERIFY (1<<10) /* do signature verification */
5252+#endif
5353+345435553656
+770
crypt.c
···11+/*
22+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
33+ * Copyright (C) 1999-2000 Thomas Roessler <roessler@guug.de>
44+ * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
55+ * Oliver Ehli <elmy@acm.org>
66+ *
77+ * This program is free software; you can redistribute it and/or modify
88+ * it under the terms of the GNU General Public License as published by
99+ * the Free Software Foundation; either version 2 of the License, or
1010+ * (at your option) any later version.
1111+ *
1212+ * This program is distributed in the hope that it will be useful,
1313+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1414+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1515+ * GNU General Public License for more details.
1616+ *
1717+ * You should have received a copy of the GNU General Public License
1818+ * along with this program; if not, write to the Free Software
1919+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
2020+ */
2121+2222+2323+#include "mutt.h"
2424+#include "mutt_curses.h"
2525+#include "crypt.h"
2626+#include "mime.h"
2727+#include "copy.h"
2828+2929+#include <sys/wait.h>
3030+#include <string.h>
3131+#include <stdlib.h>
3232+#include <unistd.h>
3333+#include <sys/stat.h>
3434+#include <errno.h>
3535+#include <ctype.h>
3636+3737+#ifdef HAVE_PGP
3838+#include "pgp.h"
3939+#endif
4040+4141+#ifdef HAVE_SMIME
4242+#include "smime.h"
4343+#endif
4444+4545+#ifdef HAVE_LOCALE_H
4646+#include <locale.h>
4747+#endif
4848+4949+#ifdef HAVE_SYS_TIME_H
5050+# include <sys/time.h>
5151+#endif
5252+5353+#ifdef HAVE_SYS_RESOURCE_H
5454+# include <sys/resource.h>
5555+#endif
5656+5757+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
5858+5959+6060+/* print the current time to avoid spoofing of the signature output */
6161+void crypt_current_time(STATE *s, char *app_name)
6262+{
6363+ time_t t;
6464+ char p[STRING], tmp[STRING];
6565+6666+ t = time(NULL);
6767+ setlocale (LC_TIME, "");
6868+ snprintf (tmp, sizeof (tmp), _("[-- %s output follows(current time: %%c) --]\n"), NONULL(app_name));
6969+ strftime (p, sizeof (p), tmp, localtime (&t));
7070+ setlocale (LC_TIME, "C");
7171+ state_attach_puts (p, s);
7272+}
7373+7474+7575+7676+void crypt_forget_passphrase (void)
7777+{
7878+7979+#ifdef HAVE_PGP
8080+ pgp_void_passphrase ();
8181+#endif
8282+8383+#ifdef HAVE_SMIME
8484+ smime_void_passphrase ();
8585+#endif
8686+8787+ mutt_message _("Passphrase(s) forgotten.");
8888+}
8989+9090+9191+# if defined(HAVE_SETRLIMIT) && (!defined(DEBUG))
9292+9393+static void disable_coredumps (void)
9494+{
9595+ struct rlimit rl = {0, 0};
9696+ static short done = 0;
9797+9898+ if (!done)
9999+ {
100100+ setrlimit (RLIMIT_CORE, &rl);
101101+ done = 1;
102102+ }
103103+}
104104+105105+# endif /* HAVE_SETRLIMIT */
106106+107107+108108+int crypt_valid_passphrase(int flags)
109109+{
110110+ time_t now = time (NULL);
111111+112112+# if defined(HAVE_SETRLIMIT) &&(!defined(DEBUG))
113113+ disable_coredumps ();
114114+# endif
115115+116116+117117+#ifdef HAVE_PGP
118118+ if (flags & APPLICATION_PGP)
119119+ {
120120+ extern char PgpPass[STRING];
121121+ extern time_t PgpExptime;
122122+123123+ if (now < PgpExptime) return 1; /* just use the cached copy. */
124124+ pgp_void_passphrase ();
125125+126126+ if (mutt_get_password (_("Enter PGP passphrase:"), PgpPass, sizeof (PgpPass)) == 0)
127127+ {
128128+ PgpExptime = time (NULL) + PgpTimeout;
129129+ return (1);
130130+ }
131131+ else
132132+ PgpExptime = 0;
133133+ }
134134+#endif
135135+#ifdef HAVE_SMIME
136136+ if (flags & APPLICATION_SMIME)
137137+ {
138138+ extern char SmimePass[STRING];
139139+ extern time_t SmimeExptime;
140140+141141+ if (now < SmimeExptime) return (1);
142142+ smime_void_passphrase ();
143143+144144+ if (mutt_get_password (_("Enter SMIME passphrase:"), SmimePass,
145145+ sizeof (SmimePass)) == 0)
146146+ {
147147+ SmimeExptime = time (NULL) + SmimeTimeout;
148148+ return (1);
149149+ }
150150+ else
151151+ SmimeExptime = 0;
152152+ }
153153+#endif
154154+ return (0);
155155+}
156156+157157+158158+159159+int mutt_protect (HEADER *msg, char *keylist)
160160+{
161161+ BODY *pbody = NULL, *tmp_pbody = NULL;
162162+#ifdef HAVE_SMIME
163163+ BODY *tmp_smime_pbody = NULL;
164164+#endif
165165+#ifdef HAVE_PGP
166166+ BODY *tmp_pgp_pbody = NULL;
167167+ int traditional = 0;
168168+ int flags = msg->security, i;
169169+#endif
170170+ if ((msg->security & SIGN) && !crypt_valid_passphrase (msg->security))
171171+ return (-1);
172172+173173+#ifdef HAVE_PGP
174174+ if (msg->security & APPLICATION_PGP)
175175+ {
176176+ if ((msg->content->type == TYPETEXT) &&
177177+ !mutt_strcasecmp (msg->content->subtype, "plain") &&
178178+ ((flags & ENCRYPT) || (msg->content->content && msg->content->content->hibin == 0)))
179179+ {
180180+ if ((i = query_quadoption (OPT_PGPTRADITIONAL, _("Create an application/pgp message?"))) == -1)
181181+ return -1;
182182+ else if (i == M_YES)
183183+ traditional = 1;
184184+ }
185185+ if (traditional)
186186+ {
187187+ mutt_message _("Invoking PGP...");
188188+ if (!(pbody = pgp_traditional_encryptsign (msg->content, flags, keylist)))
189189+ return -1;
190190+191191+ msg->content = pbody;
192192+ return 0;
193193+ }
194194+ }
195195+#endif
196196+197197+ if (!isendwin ()) mutt_endwin (NULL);
198198+199199+#ifdef HAVE_SMIME
200200+ tmp_smime_pbody = msg->content;
201201+#endif
202202+203203+204204+ if (msg->security & SIGN)
205205+ {
206206+#ifdef HAVE_SMIME
207207+ if (msg->security & APPLICATION_SMIME)
208208+ {
209209+ if (!(tmp_pbody = smime_sign_message (msg->content)))
210210+ return -1;
211211+ pbody = tmp_smime_pbody = tmp_pbody;
212212+ }
213213+#endif
214214+#ifdef HAVE_PGP
215215+ if ((msg->security & APPLICATION_PGP) &&
216216+ (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG)))
217217+ {
218218+ if (!(tmp_pbody = pgp_sign_message (msg->content)))
219219+ return -1;
220220+221221+ flags &= ~SIGN;
222222+ pbody = tmp_pgp_pbody = tmp_pbody;
223223+ }
224224+#endif
225225+226226+#if defined(HAVE_SMIME) && defined(HAVE_PGP)
227227+ if ((msg->security & APPLICATION_SMIME) &&
228228+ (msg->security & APPLICATION_PGP))
229229+ {
230230+ /* here comes the draft ;-) */
231231+ }
232232+#endif
233233+ }
234234+235235+236236+ if (msg->security & ENCRYPT)
237237+ {
238238+#ifdef HAVE_SMIME
239239+ if (msg->security & APPLICATION_SMIME)
240240+ {
241241+ if (!(tmp_pbody = smime_build_smime_entity (tmp_smime_pbody, keylist)))
242242+ {
243243+ /* signed ? free it! */
244244+ return (-1);
245245+ }
246246+ /* free tmp_body if messages was signed AND encrypted ... */
247247+ if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody)
248248+ {
249249+ /* detatch and dont't delete msg->content,
250250+ which tmp_smime_pbody->parts after signing. */
251251+ tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
252252+ msg->content->next = NULL;
253253+ mutt_free_body (&tmp_smime_pbody);
254254+ }
255255+ pbody = tmp_pbody;
256256+ }
257257+#endif
258258+259259+#ifdef HAVE_PGP
260260+ if (msg->security & APPLICATION_PGP)
261261+ {
262262+ if (!(pbody = pgp_encrypt_message (msg->content, keylist, flags & SIGN)))
263263+ {
264264+265265+ /* did we perform a retainable signature? */
266266+ if (flags != msg->security)
267267+ {
268268+ /* remove the outer multipart layer */
269269+ msg->content = mutt_remove_multipart (msg->content);
270270+ /* get rid of the signature */
271271+ mutt_free_body (&msg->content->next);
272272+ }
273273+274274+ return (-1);
275275+ }
276276+277277+ /* destroy temporary signature envelope when doing retainable
278278+ * signatures.
279279+ */
280280+ if (flags != msg->security)
281281+ {
282282+ mutt_remove_multipart (msg->content);
283283+ mutt_free_body (&msg->content->next);
284284+ }
285285+ }
286286+#endif
287287+ }
288288+289289+ if(pbody)
290290+ msg->content = pbody;
291291+292292+ return 0;
293293+}
294294+295295+296296+297297+298298+int mutt_is_multipart_signed (BODY *b)
299299+{
300300+ char *p;
301301+302302+ if (!b || !(b->type == TYPEMULTIPART) ||
303303+ !b->subtype || mutt_strcasecmp(b->subtype, "signed"))
304304+ return 0;
305305+306306+ if (!(p = mutt_get_parameter("protocol", b->parameter)))
307307+ return 0;
308308+309309+ if (!(mutt_strcasecmp (p, "multipart/mixed")))
310310+ return SIGN;
311311+312312+#ifdef HAVE_PGP
313313+ if (!(mutt_strcasecmp (p, "application/pgp-signature")))
314314+ return PGPSIGN;
315315+#endif
316316+317317+#ifdef HAVE_SMIME
318318+ if (!(mutt_strcasecmp(p, "application/x-pkcs7-signature")))
319319+ return SMIMESIGN;
320320+#endif
321321+322322+ return 0;
323323+}
324324+325325+326326+int mutt_is_multipart_encrypted (BODY *b)
327327+{
328328+ int ret=0;
329329+#ifdef HAVE_PGP
330330+ ret = pgp_is_multipart_encrypted (b);
331331+#endif
332332+333333+ return ret;
334334+}
335335+336336+337337+338338+339339+int crypt_query (BODY *m)
340340+{
341341+ int t = 0;
342342+343343+344344+ if (m->type == TYPEAPPLICATION)
345345+ {
346346+#ifdef HAVE_PGP
347347+ t |= mutt_is_application_pgp(m);
348348+#endif
349349+#ifdef HAVE_SMIME
350350+ t |= mutt_is_application_smime(m);
351351+ if (t && m->goodsig) t |= GOODSIGN;
352352+ if (t && m->badsig) t |= BADSIGN;
353353+#endif
354354+ }
355355+356356+ if (m->type == TYPEMULTIPART)
357357+ {
358358+ t |= mutt_is_multipart_encrypted(m);
359359+ t |= mutt_is_multipart_signed (m);
360360+361361+ if (t && m->goodsig) t |= GOODSIGN;
362362+ }
363363+364364+ if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE)
365365+ {
366366+ BODY *p;
367367+368368+ for (p = m->parts; p; p = p->next)
369369+ t |= crypt_query (p) & ~GOODSIGN;
370370+ }
371371+372372+ return t;
373373+}
374374+375375+376376+377377+378378+int crypt_write_signed(BODY *a, STATE *s, const char *tempfile)
379379+{
380380+ FILE *fp;
381381+ int c;
382382+ short hadcr;
383383+ size_t bytes;
384384+385385+ if (!(fp = safe_fopen (tempfile, "w")))
386386+ {
387387+ mutt_perror (tempfile);
388388+ return -1;
389389+ }
390390+391391+ fseek (s->fpin, a->hdr_offset, 0);
392392+ bytes = a->length + a->offset - a->hdr_offset;
393393+ hadcr = 0;
394394+ while (bytes > 0)
395395+ {
396396+ if ((c = fgetc (s->fpin)) == EOF)
397397+ break;
398398+399399+ bytes--;
400400+401401+ if (c == '\r')
402402+ hadcr = 1;
403403+ else
404404+ {
405405+ if (c == '\n' && !hadcr)
406406+ fputc ('\r', fp);
407407+408408+ hadcr = 0;
409409+ }
410410+411411+ fputc (c, fp);
412412+413413+ }
414414+ fclose (fp);
415415+416416+ return 0;
417417+}
418418+419419+420420+421421+void convert_to_7bit (BODY *a)
422422+{
423423+ while (a)
424424+ {
425425+ if (a->type == TYPEMULTIPART)
426426+ {
427427+ if (a->encoding != ENC7BIT)
428428+ {
429429+ a->encoding = ENC7BIT;
430430+ convert_to_7bit(a->parts);
431431+ }
432432+#ifdef HAVE_PGP
433433+ else if (option (OPTPGPSTRICTENC))
434434+ convert_to_7bit (a->parts);
435435+#endif
436436+ }
437437+ else if (a->type == TYPEMESSAGE &&
438438+ mutt_strcasecmp(a->subtype, "delivery-status"))
439439+ {
440440+ if(a->encoding != ENC7BIT)
441441+ mutt_message_to_7bit (a, NULL);
442442+ }
443443+ else if (a->encoding == ENC8BIT)
444444+ a->encoding = ENCQUOTEDPRINTABLE;
445445+ else if (a->encoding == ENCBINARY)
446446+ a->encoding = ENCBASE64;
447447+ else if (a->content && a->encoding != ENCBASE64 &&
448448+ (a->content->from || (a->content->space &&
449449+ option (OPTPGPSTRICTENC))))
450450+ a->encoding = ENCQUOTEDPRINTABLE;
451451+ a = a->next;
452452+ }
453453+}
454454+455455+456456+457457+458458+void crypt_extract_keys_from_messages (HEADER * h)
459459+{
460460+ int i;
461461+ char tempfname[_POSIX_PATH_MAX], *mbox;
462462+ ADDRESS *tmp = NULL;
463463+ FILE *fpout;
464464+465465+ mutt_mktemp (tempfname);
466466+ if (!(fpout = safe_fopen (tempfname, "w")))
467467+ {
468468+ mutt_perror (tempfname);
469469+ return;
470470+ }
471471+472472+#ifdef HAVE_PGP
473473+ set_option (OPTDONTHANDLEPGPKEYS);
474474+#endif
475475+476476+ if (!h)
477477+ {
478478+ for (i = 0; i < Context->vcount; i++)
479479+ {
480480+ if (Context->hdrs[Context->v2r[i]]->tagged)
481481+ {
482482+ mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
483483+ if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT &&
484484+ !crypt_valid_passphrase (Context->hdrs[Context->v2r[i]]->security))
485485+ {
486486+ fclose (fpout);
487487+ break;
488488+ }
489489+#ifdef HAVE_PGP
490490+ if (Context->hdrs[Context->v2r[i]]->security & APPLICATION_PGP)
491491+ {
492492+ mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
493493+ M_CM_DECODE|M_CM_CHARCONV, 0);
494494+ fflush(fpout);
495495+496496+ mutt_endwin (_("Trying to extract PGP keys...\n"));
497497+ pgp_invoke_import (tempfname);
498498+ }
499499+#endif
500500+#ifdef HAVE_SMIME
501501+ if (Context->hdrs[Context->v2r[i]]->security & APPLICATION_SMIME)
502502+ {
503503+ if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT)
504504+ mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
505505+ M_CM_NOHEADER|M_CM_DECODE_CRYPT|M_CM_DECODE_SMIME, 0);
506506+ else
507507+ mutt_copy_message (fpout, Context,
508508+ Context->hdrs[Context->v2r[i]], 0, 0);
509509+ fflush(fpout);
510510+511511+ if (Context->hdrs[Context->v2r[i]]->env->from)
512512+ tmp = mutt_expand_aliases (h->env->from);
513513+ else if (Context->hdrs[Context->v2r[i]]->env->sender)
514514+ tmp = mutt_expand_aliases (Context->hdrs[Context->v2r[i]]->env->sender);
515515+ mbox = tmp ? tmp->mailbox : NULL;
516516+ if (mbox)
517517+ {
518518+ mutt_endwin (_("Trying to extract S/MIME certificates...\n"));
519519+ smime_invoke_import (tempfname, mbox);
520520+ tmp = NULL;
521521+ }
522522+ }
523523+#endif
524524+ rewind (fpout);
525525+ }
526526+ }
527527+ }
528528+ else
529529+ {
530530+ mutt_parse_mime_message (Context, h);
531531+ if (!(h->security & ENCRYPT && !crypt_valid_passphrase (h->security)))
532532+ {
533533+#ifdef HAVE_PGP
534534+ if (h->security & APPLICATION_PGP)
535535+ {
536536+ mutt_copy_message (fpout, Context, h, M_CM_DECODE|M_CM_CHARCONV, 0);
537537+ fflush(fpout);
538538+ mutt_message (_("Trying to extract PGP keys...\n"));
539539+ pgp_invoke_import (tempfname);
540540+ }
541541+#endif
542542+#ifdef HAVE_SMIME
543543+ if (h->security & APPLICATION_SMIME)
544544+ {
545545+ if (h->security & ENCRYPT)
546546+ mutt_copy_message (fpout, Context, h, M_CM_NOHEADER|M_CM_DECODE_CRYPT|M_CM_DECODE_SMIME, 0);
547547+ else
548548+ mutt_copy_message (fpout, Context, h, 0, 0);
549549+550550+ fflush(fpout);
551551+ if (h->env->from) tmp = mutt_expand_aliases (h->env->from);
552552+ else if (h->env->sender) tmp = mutt_expand_aliases (h->env->sender);
553553+ mbox = tmp ? tmp->mailbox : NULL;
554554+ if (mbox) /* else ? */
555555+ {
556556+ mutt_message (_("Trying to extract S/MIME certificates...\n"));
557557+ smime_invoke_import (tempfname, mbox);
558558+ }
559559+ }
560560+#endif
561561+ }
562562+ }
563563+564564+ fclose (fpout);
565565+ mutt_any_key_to_continue (NULL);
566566+567567+ mutt_unlink (tempfname);
568568+569569+#ifdef HAVE_PGP
570570+ unset_option (OPTDONTHANDLEPGPKEYS);
571571+#endif
572572+}
573573+574574+575575+576576+int crypt_get_keys (HEADER *msg, char **keylist)
577577+{
578578+ /* Do a quick check to make sure that we can find all of the encryption
579579+ * keys if the user has requested this service.
580580+ */
581581+582582+#ifdef HAVE_SMIME
583583+ extern char *smime_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc);
584584+#endif
585585+#ifdef HAVE_PGP
586586+ extern char *pgp_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc);
587587+588588+ set_option (OPTPGPCHECKTRUST);
589589+590590+#endif
591591+592592+ *keylist = NULL;
593593+594594+595595+ if (msg->security & ENCRYPT)
596596+ {
597597+#ifdef HAVE_PGP
598598+ if (msg->security & APPLICATION_PGP)
599599+ {
600600+ if ((*keylist = pgp_findKeys (msg->env->to, msg->env->cc,
601601+ msg->env->bcc)) == NULL)
602602+ return (-1);
603603+ unset_option (OPTPGPCHECKTRUST);
604604+ }
605605+#endif
606606+#ifdef HAVE_SMIME
607607+ if (msg->security & APPLICATION_SMIME)
608608+ {
609609+ if ((*keylist = smime_findKeys (msg->env->to, msg->env->cc,
610610+ msg->env->bcc)) == NULL)
611611+ return (-1);
612612+ }
613613+#endif
614614+ }
615615+616616+ return (0);
617617+}
618618+619619+620620+621621+static void crypt_fetch_signatures (BODY ***signatures, BODY *a, int *n)
622622+{
623623+ for (; a; a = a->next)
624624+ {
625625+ if (a->type == TYPEMULTIPART)
626626+ crypt_fetch_signatures (signatures, a->parts, n);
627627+ else
628628+ {
629629+ if((*n % 5) == 0)
630630+ safe_realloc ((void **) signatures, (*n + 6) * sizeof (BODY **));
631631+632632+ (*signatures)[(*n)++] = a;
633633+ }
634634+ }
635635+}
636636+637637+638638+/*
639639+ * This routine verifies a "multipart/signed" body.
640640+ */
641641+642642+void mutt_signed_handler (BODY *a, STATE *s)
643643+{
644644+ char tempfile[_POSIX_PATH_MAX];
645645+ char *protocol;
646646+ int protocol_major = TYPEOTHER;
647647+ char *protocol_minor = NULL;
648648+649649+ BODY *b = a;
650650+ BODY **signatures = NULL;
651651+ int sigcnt = 0;
652652+ int i;
653653+ short goodsig = 1;
654654+655655+ protocol = mutt_get_parameter ("protocol", a->parameter);
656656+ a = a->parts;
657657+658658+ /* extract the protocol information */
659659+660660+ if (protocol)
661661+ {
662662+ char major[STRING];
663663+ char *t;
664664+665665+ if ((protocol_minor = strchr (protocol, '/'))) protocol_minor++;
666666+667667+ strfcpy (major, protocol, sizeof(major));
668668+ if((t = strchr(major, '/')))
669669+ *t = '\0';
670670+671671+ protocol_major = mutt_check_mime_type (major);
672672+ }
673673+674674+ /* consistency check */
675675+676676+ if (!(a && a->next && a->next->type == protocol_major &&
677677+ !mutt_strcasecmp (a->next->subtype, protocol_minor)))
678678+ {
679679+ state_attach_puts (_("[-- Error: Inconsistent multipart/signed structure! --]\n\n"), s);
680680+ mutt_body_handler (a, s);
681681+ return;
682682+ }
683683+684684+685685+#ifdef HAVE_PGP
686686+ if (protocol_major == TYPEAPPLICATION &&
687687+ !mutt_strcasecmp (protocol_minor, "pgp-signature"));
688688+#endif
689689+#if defined(HAVE_PGP) && defined(HAVE_SMIME)
690690+ else
691691+#endif
692692+#ifdef HAVE_SMIME
693693+ if (protocol_major == TYPEAPPLICATION &&
694694+ !mutt_strcasecmp (protocol_minor, "x-pkcs7-signature"));
695695+696696+#endif
697697+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
698698+ else
699699+#endif
700700+ if (protocol_major == TYPEMULTIPART &&
701701+ !mutt_strcasecmp (protocol_minor, "mixed"));
702702+703703+ else
704704+ {
705705+ state_printf (s, _("[-- Error: Unknown multipart/signed protocol %s! --]\n\n"), protocol);
706706+ mutt_body_handler (a, s);
707707+ return;
708708+ }
709709+710710+ if (s->flags & M_DISPLAY)
711711+ {
712712+713713+ crypt_fetch_signatures (&signatures, a->next, &sigcnt);
714714+715715+ if (sigcnt)
716716+ {
717717+ mutt_mktemp (tempfile);
718718+ if (crypt_write_signed (a, s, tempfile) == 0)
719719+ {
720720+ for (i = 0; i < sigcnt; i++)
721721+ {
722722+#ifdef HAVE_PGP
723723+ if (signatures[i]->type == TYPEAPPLICATION
724724+ && !mutt_strcasecmp (signatures[i]->subtype, "pgp-signature"))
725725+ {
726726+ if (pgp_verify_one (signatures[i], s, tempfile) != 0)
727727+ goodsig = 0;
728728+729729+ continue;
730730+ }
731731+#endif
732732+#ifdef HAVE_SMIME
733733+ if (signatures[i]->type == TYPEAPPLICATION
734734+ && !mutt_strcasecmp(signatures[i]->subtype, "x-pkcs7-signature"))
735735+ {
736736+ if (smime_verify_one (signatures[i], s, tempfile) != 0)
737737+ goodsig = 0;
738738+739739+ continue;
740740+ }
741741+#endif
742742+ state_printf (s, _("[-- Warning: We can't verify %s/%s signatures. --]\n\n"),
743743+ TYPE(signatures[i]), signatures[i]->subtype);
744744+ }
745745+ }
746746+747747+ mutt_unlink (tempfile);
748748+749749+ b->goodsig = goodsig;
750750+ b->badsig = goodsig;
751751+752752+ /* Now display the signed body */
753753+ state_attach_puts (_("[-- The following data is signed --]\n\n"), s);
754754+755755+756756+ safe_free((void **) &signatures);
757757+ }
758758+ else
759759+ state_attach_puts (_("[-- Warning: Can't find any signatures. --]\n\n"), s);
760760+ }
761761+762762+ mutt_body_handler (a, s);
763763+764764+ if (s->flags & M_DISPLAY && sigcnt)
765765+ state_attach_puts (_("\n[-- End of signed data --]\n"), s);
766766+}
767767+768768+769769+770770+#endif
+62
crypt.h
···11+/*
22+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
33+ * Copyright (C) 1999-2000 Thomas Roessler <roessler@guug.de>
44+ * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
55+ * Oliver Ehli <elmy@acm.org>
66+ *
77+ * This program is free software; you can redistribute it and/or modify
88+ * it under the terms of the GNU General Public License as published by
99+ * the Free Software Foundation; either version 2 of the License, or
1010+ * (at your option) any later version.
1111+ *
1212+ * This program is distributed in the hope that it will be useful,
1313+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1414+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1515+ * GNU General Public License for more details.
1616+ *
1717+ * You should have received a copy of the GNU General Public License
1818+ * along with this program; if not, write to the Free Software
1919+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
2020+ */
2121+2222+2323+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
2424+2525+#define ENCRYPT (1 << 0)
2626+#define SIGN (1 << 1)
2727+#define GOODSIGN (1 << 2)
2828+#define BADSIGN (1 << 3)
2929+3030+int mutt_protect (HEADER *, char *);
3131+3232+int mutt_is_multipart_encrypted (BODY *);
3333+3434+int mutt_is_multipart_signed (BODY *);
3535+3636+void mutt_signed_handler (BODY *, STATE *);
3737+3838+int mutt_parse_crypt_hdr (char *, int);
3939+4040+int crypt_query (BODY *);
4141+4242+void crypt_extract_keys_from_messages (HEADER *);
4343+4444+int crypt_get_keys (HEADER *, char **);
4545+4646+4747+void crypt_forget_passphrase (void);
4848+4949+int crypt_valid_passphrase (int);
5050+5151+5252+int crypt_write_signed(BODY *, STATE *, const char *);
5353+5454+void convert_to_7bit (BODY *);
5555+5656+5757+/* private ? */
5858+5959+void crypt_current_time(STATE *, char *);
6060+6161+6262+#endif
···11+From raldi@research.netsol.com Thu Jan 24 18:33:03 2002
22+Date: Thu, 24 Jan 2002 11:44:40 -0500
33+From: Mike Schiraldi <raldi@research.netsol.com>
44+To: Thomas Roessler <roessler@does-not-exist.org>
55+Subject: Re: Where's the S/MIME patch? ;-)
66+User-Agent: Mutt/1.3.27i
77+Mime-Version: 1.0
88+99+I wanted to test this patch off CVS, but with or without the patch, i
1010+couldn't get autoconf/automake/aclocal/configure to work.
1111+1212+1313+Files:
1414+1515+smime.patch -- the S/MIME patch
1616+smime_keys.pl -- the perl script
1717+smime.rc -- a bunch of commands you'll need to source into your .muttrc
1818+1919+ca-bundle.crt -- a collection of root certificates
2020+2121+color.patch -- the color patch
2222+color.rc -- the muttrc commands it uses
2323+2424+Okay, here's what you do:
2525+2626+Apply smime.patch.
2727+2828+Build.
2929+3030+Get yourself a certificate (You can get one for free from www.thawte.com, or
3131+i could buy you a VeriSign certificate, or you could go to one of our
3232+competitors). The way this process works, the certificate will be installed
3333+"into" your web browser.
3434+3535+Assuming you're using Mozilla on Linux, follow the instructions at
3636+www.verisignlabs.com/Projects/smime_docs/linux.html to export the
3737+certificate into a file called cert.p12.
3838+3939+Run smime_keys.pl add_p12 cert.p12
4040+4141+ * When the script asks for the "Import password", enter the one you
4242+ provided when you exported the certificate.
4343+4444+ * When it asks for a "PEM pass phrase", make up a new password. Every
4545+ time you sign or decrypt a message, mutt will ask for the PEM pass
4646+ phrase.
4747+4848+ * Finally, when the script asks for a label, enter an easy-to-remember
4949+ name for the certificate, such as "me". The script output will include
5050+ a line like:
5151+5252+ added private key: /home/raldi/.smime/keys/12345678.0 for raldi@verisignlabs.com
5353+5454+ The number is your keyid. You will need this number in the next step.
5555+5656+Put your keyid into smime.muttrc's smime_sign_as line.
5757+5858+Source smime.muttrc into your .muttrc
5959+6060+6161+You probably want to import the trusted roots in ca-bundle.crt. Use
6262+"smime_keys.pl add_root" to do so. That makes you trust anything that was
6363+ultimately signed by one of them.
6464+6565+To try out the color patch, just apply it and source the color.rc. You can
6666+see the results in any folder with signed messages.
6767+6868+6969+--
7070+Mike Schiraldi
7171+VeriSign Applied Research
7272+7373+7474+7575+7676+
···9090# ifndef HAVE_PGP
9191# define HAVE_PGP
9292# endif
9393+# ifndef HAVE_SMIME
9494+# define HAVE_SMIME
9595+# endif
9396# ifndef USE_POP
9497# define USE_POP
9598# endif
···501504 ** This variable controls whether or not attachments on outgoing messages
502505 ** are saved along with the main body of your message.
503506 */
504504-#ifdef HAVE_PGP
507507+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
505508 { "fcc_clear", DT_BOOL, R_NONE, OPTFCCCLEAR, 0 },
506509 /*
507510 ** .pp
···11461149 */
114711501148115111491149-#ifdef HAVE_PGP
11501150-11511151- { "pgp_autosign", DT_BOOL, R_NONE, OPTPGPAUTOSIGN, 0 },
11521152+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
11531153+# ifdef HAVE_PGP
11541154+ { "pgp_autosign", DT_SYN, R_NONE, UL "crypt_autosign", 0 },
11551155+# endif
11561156+ { "crypt_autosign", DT_BOOL, R_NONE, OPTCRYPTAUTOSIGN, 0 },
11521157 /*
11531158 ** .pp
11541154- ** Setting this variable will cause Mutt to always attempt to PGP/MIME
11551155- ** sign outgoing messages. This can be overridden by use of the \fIpgp-
11561156- ** menu\fP, when signing is not required or encryption is requested as
11571157- ** well.
11591159+ ** Setting this variable will cause Mutt to always attempt to
11601160+ ** cryptographically sign outgoing messages. This can be overridden
11611161+ ** by use of the \fIpgp-menu\fP, when signing is not required or
11621162+ ** encryption is requested as well. IF ``$$smime_is_default'' is set,
11631163+ ** then OpenSSL is used instead to create S/MIME messages and settings can
11641164+ ** be overridden by use of the \fIsmime-menu\fP.
11581165 */
11591159- { "pgp_autoencrypt", DT_BOOL, R_NONE, OPTPGPAUTOENCRYPT, 0 },
11661166+# ifdef HAVE_PGP
11671167+ { "pgp_autoencrypt", DT_SYN, R_NONE, UL "crypt_autoencrypt", 0 },
11681168+# endif
11691169+ { "crypt_autoencrypt", DT_BOOL, R_NONE, OPTCRYPTAUTOENCRYPT, 0 },
11601170 /*
11611171 ** .pp
11621172 ** Setting this variable will cause Mutt to always attempt to PGP/MIME
11631173 ** encrypt outgoing messages. This is probably only useful in connection
11641174 ** to the \fIsend-hook\fP command. It can be overridden by use of the
11651175 ** \fIpgp-menu\fP, when encryption is not required or signing is
11661166- ** requested as well.
11761176+ ** requested as well. IF ``$$smime_is_default'' is set, then
11771177+ ** OpenSSL is used instead to create S/MIME messages and settings can
11781178+ ** be overridden by use of the \fIsmime-menu\fP.
11671179 */
11801180+#ifdef HAVE_PGP
11681181 { "pgp_ignore_subkeys", DT_BOOL, R_NONE, OPTPGPIGNORESUB, 1},
11691182 /*
11701183 ** .pp
···11721185 ** the principal key will inherit the subkeys' capabilities. Unset this
11731186 ** if you want to play interesting key selection games.
11741187 */
11881188+#endif
11891189+#ifdef HAVE_PGP
11901190+ { "pgp_replyencrypt", DT_SYN, R_NONE, UL "crypt_replyencrypt", 0 },
11911191+#endif
11921192+ { "crypt_replyencrypt", DT_BOOL, R_NONE, OPTCRYPTREPLYENCRYPT, 0 },
11931193+ /*
11941194+ ** .pp
11951195+ ** If set, automatically PGP or OpenSSL encrypt replies to messages which are
11961196+ ** encrypted.
11971197+ */
11981198+#ifdef HAVE_PGP
11991199+ { "pgp_replysign", DT_SYN, R_NONE, UL "crypt_replysign", 0 },
12001200+#endif
12011201+ { "crypt_replysign", DT_BOOL, R_NONE, OPTCRYPTREPLYSIGN, 0 },
12021202+ /*
12031203+ ** .pp
12041204+ ** If set, automatically PGP or OpenSSL sign replies to messages which are
12051205+ ** signed.
12061206+ ** .pp
12071207+ ** \fBNote:\fP this does not work on messages that are encrypted
12081208+ ** \fBand\fP signed!
12091209+ */
12101210+#ifdef HAVE_PGP
12111211+ { "pgp_replysignencrypted", DT_SYN, R_NONE, UL "crypt_replysignencrypted", 0},
12121212+#endif
12131213+ { "crypt_replysignencrypted", DT_BOOL, R_NONE, OPTCRYPTREPLYSIGNENCRYPTED, 0 },
12141214+ /*
12151215+ ** .pp
12161216+ ** If set, automatically PGP/OpenSSL sign replies to messages which are
12171217+ ** encrypted. This makes sense in combination with
12181218+ ** ``$$crypt_replyencrypt'', because it allows you to sign all messages
12191219+ ** which are automatically encrypted. This works around the problem
12201220+ ** noted in ``$$crypt_replysign'', that mutt is not able to find out
12211221+ ** whether an encrypted message is also signed.
12221222+ */
12231223+#ifdef HAVE_PGP
12241224+ { "pgp_verify_sig", DT_SYN, R_NONE, UL "crypt_verify_sig", 0},
12251225+#endif
12261226+ { "crypt_verify_sig", DT_QUAD, R_NONE, OPT_VERIFYSIG, M_YES },
12271227+ /*
12281228+ ** .pp
12291229+ ** If ``yes'', always attempt to verify PGP/MIME or S/MIME signatures.
12301230+ ** If ``ask'', ask whether or not to verify the signature.
12311231+ ** If ``no'', never attempt to verify cryptographic signatures.
12321232+ */
12331233+#endif /* defined(HAVE_PGP) || defined(HAVE_SMIME) */
12341234+#ifdef HAVE_SMIME
12351235+ { "smime_is_default", DT_BOOL, R_NONE, OPTSMIMEISDEFAULT, 0},
12361236+ /*
12371237+ ** .pp
12381238+ ** The default behaviour of mutt is to use PGP on all auto-sign/encryption
12391239+ ** operations. To override and to use OpenSSL instead this must be set.
12401240+ ** However, this has no effect while replying, since mutt will automatically
12411241+ ** select the same application that was used to sign/encrypt the original
12421242+ ** message.
12431243+ */
12441244+#endif
12451245+#ifdef HAVE_PGP
11751246 { "pgp_entry_format", DT_STR, R_NONE, UL &PgpEntryFormat, UL "%4n %t%f %4l/0x%k %-4a %2c %u" },
11761247 /*
11771248 ** .pp
···12041275 ** .pp
12051276 ** If set, use 64 bit PGP key IDs. Unset uses the normal 32 bit Key IDs.
12061277 */
12071207- { "pgp_replyencrypt", DT_BOOL, R_NONE, OPTPGPREPLYENCRYPT, 1 },
12081208- /*
12091209- ** .pp
12101210- ** If set, automatically PGP encrypt replies to messages which are
12111211- ** encrypted.
12121212- */
12131213- { "pgp_replysign", DT_BOOL, R_NONE, OPTPGPREPLYSIGN, 0 },
12141214- /*
12151215- ** .pp
12161216- ** If set, automatically PGP sign replies to messages which are signed.
12171217- ** .pp
12181218- ** \fBNote:\fP this does not work on messages that are encrypted
12191219- ** \fBand\fP signed!
12201220- */
12211221- { "pgp_replysignencrypted", DT_BOOL, R_NONE, OPTPGPREPLYSIGNENCRYPTED, 0 },
12221222- /*
12231223- ** .pp
12241224- ** If set, automatically PGP sign replies to messages which are
12251225- ** encrypted. This makes sense in combination with
12261226- ** ``$$pgp_replyencrypt'', because it allows you to sign all messages
12271227- ** which are automatically encrypted. This works around the problem
12281228- ** noted in ``$$pgp_replysign'', that mutt is not able to find out
12291229- ** whether an encrypted message is also signed.
12301230- */
12311278 { "pgp_retainable_sigs", DT_BOOL, R_NONE, OPTPGPRETAINABLESIG, 0 },
12321279 /*
12331280 ** .pp
···12651312 ** .pp
12661313 ** The number of seconds after which a cached passphrase will expire if
12671314 ** not used.
12681268- */
12691269- { "pgp_verify_sig", DT_QUAD, R_NONE, OPT_VERIFYSIG, M_YES },
12701270- /*
12711271- ** .pp
12721272- ** If ``yes'', always attempt to verify PGP/MIME signatures. If ``ask-yes''
12731273- ** or ``ask-no'',
12741274- ** ask whether or not to verify the signature. If ``no'', never attempt
12751275- ** to verify PGP/MIME signatures.
12761315 */
12771316 { "pgp_sort_keys", DT_SORT|DT_SORT_KEYS, R_NONE, UL &PgpSortKeys, SORT_ADDRESS },
12781317 /*
···14181457 */
14191458#endif /* HAVE_PGP */
1420145914601460+#ifdef HAVE_SMIME
14611461+ { "smime_timeout", DT_NUM, R_NONE, UL &SmimeTimeout, 300 },
14621462+ /*
14631463+ ** .pp
14641464+ ** The number of seconds after which a cached passphrase will expire if
14651465+ ** not used.
14661466+ */
14671467+ { "smime_encrypt_with", DT_STR, R_NONE, UL &SmimeCryptAlg, 0 },
14681468+ /*
14691469+ ** .pp
14701470+ ** This sets the algorithm that should be used for encryption.
14711471+ ** Valid choices are "des", "des3", "rc2-40", "rc2-64", "rc2-128".
14721472+ ** If unset "3des" (TrippleDES) is used.
14731473+ */
14741474+ { "smime_keys", DT_PATH, R_NONE, UL &SmimeKeys, 0 },
14751475+ /*
14761476+ ** .pp
14771477+ ** Since there is no pubring/secring as with PGP, mutt has to handle
14781478+ ** storage ad retrieval of keys by itself. This is very basic right now,
14791479+ ** and stores keys and certificates in two different directories, both
14801480+ ** named as the hash-value retrieved from OpenSSl. There is an index file
14811481+ ** which contains mailbox-address keyid pai, and which can be manually
14821482+ ** edited.
14831483+ */
14841484+ { "smime_certificates", DT_PATH, R_NONE, UL &SmimeCertificates, 0 },
14851485+ /*
14861486+ ** .pp
14871487+ ** Since there is no pubring/secring as with PGP, mutt has to handle
14881488+ ** storage ad retrieval of keys by itself. This is very basic right now,
14891489+ ** and stores keys and certificates in two different directories, both
14901490+ ** named as the hash-value retrieved from OpenSSl. There is an index file
14911491+ ** which contains mailbox-address keyid pai, and which can be manually
14921492+ ** edited.
14931493+ */
14941494+ { "smime_decrypt_command", DT_STR, R_NONE, UL &SmimeDecryptCommand, 0},
14951495+ /*
14961496+ ** .pp
14971497+ ** This format strings specifies a command which is used to decrypt
14981498+ ** application/x-pkcs7-mime attachments.
14991499+ ** .pp
15001500+ ** The OpenSSL command formats have their own set of printf-like sequences
15011501+ ** similar to PGP's:
15021502+ ** .pp
15031503+ ** .dl
15041504+ ** .dt %f .dd Expands to the name of a file containing a message.
15051505+ ** .dt %s .dd Expands to the name of a file containing the signature part
15061506+ ** . of a multipart/signed attachment when verifying it.
15071507+ ** .dt %k .dd The key-pair specified with $$smime_sign_as.
15081508+ ** .dt %c .dd One or more certificate IDs.
15091509+ ** .dt %a .dd The algorithm used for encryption.
15101510+ ** .de
15111511+ ** .pp
15121512+ ** For examples on how to configure these formats, see the smime.rc
15131513+ ** the samples/ subdirectory which has been installed on your system
15141514+ ** alongside the documentation.
15151515+ */
15161516+ { "smime_verify_command", DT_STR, R_NONE, UL &SmimeVerifyCommand, 0},
15171517+ /*
15181518+ ** .pp
15191519+ ** This command is used to verify S/MIME signatures of type multipart/signed.
15201520+ */
15211521+ { "smime_verify_opaque_command", DT_STR, R_NONE, UL &SmimeVerifyOpaqueCommand, 0},
15221522+ /*
15231523+ ** .pp
15241524+ ** This command is used to verify S/MIME signatures of type
15251525+ ** application/x-pkcs7-mime.
15261526+ */
15271527+ { "smime_sign_command", DT_STR, R_NONE, UL &SmimeSignCommand, 0},
15281528+ /*
15291529+ ** .pp
15301530+ ** This command is used to created S/MIME signatures of type
15311531+ ** multipart/signed, which can be read by all mail clients.
15321532+ */
15331533+ { "smime_sign_opaque_command", DT_STR, R_NONE, UL &SmimeSignOpaqueCommand, 0},
15341534+ /*
15351535+ ** .pp
15361536+ ** This command is used to created S/MIME signatures of type
15371537+ ** applicatipn/x-pkcs7-signature, which can only be handled by mail
15381538+ ** clients supporting the S/MIME extension
15391539+ */
15401540+ { "smime_encrypt_command", DT_STR, R_NONE, UL &SmimeEncryptCommand, 0},
15411541+ /*
15421542+ ** .pp
15431543+ ** This command is used to create encrypted S/MIME messages.
15441544+ */
15451545+ { "smime_pk7out_command", DT_STR, R_NONE, UL &SmimePk7outCommand, 0},
15461546+ /*
15471547+ ** .pp
15481548+ ** This command is used to extract PKCS7 structures of S/MIME signatures,
15491549+ ** in Order to extract the public X509 certificate(s).
15501550+ */
15511551+ { "smime_get_cert_command", DT_STR, R_NONE, UL &SmimeGetCertCommand, 0},
15521552+ /*
15531553+ ** .pp
15541554+ ** This command is used to extract X509 certificates from a PKCS7 structure.
15551555+ */
15561556+ { "smime_get_signer_cert_command", DT_STR, R_NONE, UL &SmimeGetSignerCertCommand, 0},
15571557+ /*
15581558+ ** .pp
15591559+ ** This command is used to extract only the signers X509 certificate from a S/MIME signature,
15601560+ ** so that the certificate's owner may get compared to the email's from field.
15611561+ */
15621562+ { "smime_hash_cert_command", DT_STR, R_NONE, UL &SmimeHashCertCommand, 0},
15631563+ /*
15641564+ ** .pp
15651565+ ** This command is used to calculate a hash value used for storing
15661566+ ** X509 certificates.
15671567+ */
15681568+ { "smime_get_cert_email_command", DT_STR, R_NONE, UL &SmimeGetCertEmailCommand, 0},
15691569+ /*
15701570+ ** .pp
15711571+ ** This command is used to extract the mail address used for storing
15721572+ ** X509 certificates, abd for verification purposes (to see if the
15731573+ ** certifacate was issued for the sender's mailbox.
15741574+ */
15751575+ { "smime_sign_as", DT_STR, R_NONE, UL &SmimeSignAs, 0 },
15761576+ /*
15771577+ ** .pp
15781578+ ** This is the default key-pair to use vor signing. This must be set to the
15791579+ ** keyid (the hash-value, OpenSSL generates) to work properly (key handling
15801580+ ** is very limited right now.)
15811581+ */
15821582+#endif /* HAVE_SMIME */
15831583+14211584#if defined(USE_SSL)||defined(USE_NSS)
14221585# ifndef USE_NSS
14231586 { "ssl_starttls", DT_QUAD, R_NONE, OPT_SSLSTARTTLS, M_YES },
···24602623 { "mono", mutt_parse_mono, 0 },
24612624 { "my_hdr", parse_my_hdr, 0 },
24622625#ifdef HAVE_PGP
24632463- { "pgp-hook", mutt_parse_hook, M_PGPHOOK },
26262626+ { "pgp-hook", mutt_parse_hook, M_CRYPTHOOK },
26272627+#endif
26282628+#if defined(HAVE_PGP) || defined(HAVE_SMIME)
26292629+ { "crypt-hook", mutt_parse_hook, M_CRYPTHOOK },
24642630#endif /* HAVE_PGP */
24652631 { "push", mutt_parse_push, 0 },
24662632 { "reset", parse_set, M_SET_RESET },
···11+/*
22+ * Copyright (C) 2001 Oliver Ehli <elmy@acm.org>
33+ *
44+ * This program is free software; you can redistribute it and/or modify
55+ * it under the terms of the GNU General Public License as published by
66+ * the Free Software Foundation; either version 2 of the License, or
77+ * (at your option) any later version.
88+ *
99+ * This program is distributed in the hope that it will be useful,
1010+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1212+ * GNU General Public License for more details.
1313+ *
1414+ * You should have received a copy of the GNU General Public License
1515+ * along with this program; if not, write to the Free Software
1616+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
1717+ */
1818+1919+2020+#ifdef HAVE_SMIME
2121+2222+#include "crypt.h"
2323+2424+WHERE char *SmimeSignAs;
2525+WHERE char *SmimeCryptAlg;
2626+WHERE short SmimeTimeout;
2727+WHERE char *SmimeCertificates;
2828+WHERE char *SmimeKeys;
2929+WHERE char *SmimeCryptAlg;
3030+3131+/* The command formats */
3232+3333+WHERE char *SmimeVerifyCommand;
3434+WHERE char *SmimeVerifyOpaqueCommand;
3535+WHERE char *SmimeDecryptCommand;
3636+3737+WHERE char *SmimeSignCommand;
3838+WHERE char *SmimeSignOpaqueCommand;
3939+WHERE char *SmimeEncryptCommand;
4040+4141+WHERE char *SmimeGetSignerCertCommand;
4242+WHERE char *SmimePk7outCommand;
4343+WHERE char *SmimeGetCertCommand;
4444+WHERE char *SmimeHashCertCommand;
4545+WHERE char *SmimeGetCertEmailCommand;
4646+4747+#define APPLICATION_SMIME (1 << 6)
4848+4949+#define SIGNOPAQUE (1 << 4)
5050+5151+#define SMIMEENCRYPT (APPLICATION_SMIME | ENCRYPT)
5252+#define SMIMESIGN (APPLICATION_SMIME | SIGN)
5353+#define SMIMEGOODSIGN (APPLICATION_SMIME | GOODSIGN)
5454+#define SMIMEBADSIGN (APPLICATION_SMIME | BADSIGN)
5555+#define SMIMEOPAQUE (APPLICATION_SMIME | SIGNOPAQUE)
5656+5757+5858+5959+#define smime_valid_passphrase() crypt_valid_passphrase(APPLICATION_SMIME)
6060+6161+void smime_void_passphrase (void);
6262+6363+int mutt_is_application_smime (BODY *);
6464+6565+6666+6767+6868+int smime_decrypt_mime (FILE *, FILE **, BODY *, BODY **);
6969+7070+void smime_application_smime_handler (BODY *, STATE *);
7171+7272+int smime_verify_sender(HEADER *);
7373+7474+7575+7676+7777+char *smime_get_field_from_db (char *, char *, short);
7878+7979+char* smime_ask_for_key (char *, char *, short);
8080+8181+void smime_getkeys (char *);
8282+8383+8484+/* private ? */
8585+8686+void smime_invoke_import (char *, char *);
8787+8888+int smime_verify_one(BODY *, STATE *, const char *);
8989+9090+BODY *smime_sign_message (BODY *);
9191+9292+BODY *smime_build_smime_entity (BODY *, char *);
9393+#endif
+887
smime_keys.pl
···11+#!/usr/bin/perl -w
22+33+# Settings:
44+55+my $SmimeMuttrc="$ENV{HOME}/.mutt/muttrc";
66+77+# Copyright (C) 2001 Oliver Ehli <elmy@acm.org>
88+# Copyright (C) 2001 Mike Schiraldi <raldi@research.netsol.com>
99+#
1010+# This program is free software; you can redistribute it and/or modify
1111+# it under the terms of the GNU General Public License as published by
1212+# the Free Software Foundation; either version 2 of the License, or
1313+# (at your option) any later version.
1414+#
1515+# This program is distributed in the hope that it will be useful,
1616+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1717+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1818+# GNU General Public License for more details.
1919+#
2020+# You should have received a copy of the GNU General Public License
2121+# along with this program; if not, write to the Free Software
2222+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
2323+2424+use strict;
2525+2626+require "timelocal.pl";
2727+2828+# Global variables:
2929+3030+my $private_keys_path;
3131+my $certificates_path;
3232+my $root_certs_switch;
3333+my $root_certs_path;
3434+3535+3636+sub usage ();
3737+sub get_paths ($ );
3838+sub myglob ($ );
3939+4040+# directory setup routines
4141+sub mkdir_recursive ($;$ );
4242+sub init_paths ();
4343+4444+# key/certificate management methods
4545+sub list_certs ();
4646+sub query_label ();
4747+sub add_entry ($$$$;$ );
4848+sub add_certificate ($$$$;$ );
4949+sub add_key ($$$$);
5050+sub add_root_cert ($);
5151+sub parse_pem (@ );
5252+sub handle_pem (@ );
5353+sub modify_entry ($$$;$ );
5454+sub remove_pair ($ );
5555+sub change_label ($ );
5656+sub verify_cert($;$ );
5757+sub do_verify($$;$);
5858+5959+6060+6161+6262+# Get the directories mutt uses for certificate/key storage.
6363+6464+($private_keys_path, $certificates_path,
6565+ $root_certs_switch, $root_certs_path) = get_paths($SmimeMuttrc);
6666+6767+$certificates_path and $private_keys_path
6868+ and $root_certs_switch and $root_certs_path or
6969+ die("Couldn't get paths to certificates/keys from $SmimeMuttrc");
7070+7171+#
7272+# OPS
7373+#
7474+7575+if(@ARGV == 1 and $ARGV[0] eq "init") {
7676+ init_paths;
7777+}
7878+elsif(@ARGV == 1 and $ARGV[0] eq "list") {
7979+ list_certs;
8080+}
8181+elsif(@ARGV == 2 and $ARGV[0] eq "label") {
8282+ change_label($ARGV[1]);
8383+}
8484+elsif(@ARGV == 2 and $ARGV[0] eq "add_cert") {
8585+ my $cmd = "openssl x509 -noout -hash -in $ARGV[1]";
8686+ my $cert_hash = `$cmd`;
8787+ $? and die "'$cmd' returned $?";
8888+ chomp($cert_hash);
8989+ my $label = query_label;
9090+ &add_certificate($ARGV[1], \$cert_hash, 1, $label, '-');
9191+}
9292+elsif(@ARGV == 2 and $ARGV[0] eq "add_pem") {
9393+ -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
9494+ open(PEM_FILE, "<$ARGV[1]") or die("Can't open $ARGV[1]: $!");
9595+ my @pem = <PEM_FILE>;
9696+ close(PEM_FILE);
9797+ handle_pem(@pem);
9898+}
9999+elsif( @ARGV == 2 and $ARGV[0] eq "add_p12") {
100100+ -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
101101+102102+ print "\nNOTE: This will ask you for two passphrases:\n";
103103+ print " 1. The passphrase you used for exporting\n";
104104+ print " 2. The passphrase you wish to secure your private key with.\n\n";
105105+106106+ my $pem_file = "$ARGV[1].pem";
107107+108108+ my $cmd = "openssl pkcs12 -in $ARGV[1] -out $pem_file";
109109+ system $cmd and die "'$cmd' returned $?";
110110+111111+ -e $pem_file and -s $pem_file or die("Conversion of $ARGV[1] failed.");
112112+ open(PEM_FILE, $pem_file) or die("Can't open $pem_file: $!");
113113+ my @pem = <PEM_FILE>;
114114+ close(PEM_FILE);
115115+ handle_pem(@pem);
116116+}
117117+elsif(@ARGV == 4 and $ARGV[0] eq "add_chain") {
118118+ my $cmd = "openssl x509 -noout -hash -in $ARGV[2]";
119119+ my $cert_hash = `$cmd`;
120120+ $? and die "'$cmd' returned $?";
121121+122122+ $cmd = "openssl x509 -noout -hash -in $ARGV[3]";
123123+ my $issuer_hash = `$cmd`;
124124+ $? and die "'$cmd' returned $?";
125125+126126+ chomp($cert_hash);
127127+ chomp($issuer_hash);
128128+129129+ my $label = query_label;
130130+131131+ add_certificate($ARGV[3], \$issuer_hash, 0, $label);
132132+ my $mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash);
133133+134134+ add_key($ARGV[1], $cert_hash, $mailbox, $label);
135135+}
136136+elsif((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") {
137137+ verify_cert($ARGV[1], $ARGV[2]);
138138+}
139139+elsif(@ARGV == 2 and $ARGV[0] eq "remove") {
140140+ remove_pair($ARGV[1]);
141141+}
142142+elsif(@ARGV == 2 and $ARGV[0] eq "add_root") {
143143+ add_root_cert($ARGV[1]);
144144+}
145145+else {
146146+ usage;
147147+ exit(1);
148148+}
149149+150150+exit(0);
151151+152152+153153+154154+155155+156156+############## sub-routines ########################
157157+158158+sub usage () {
159159+ print <<EOF;
160160+161161+Usage: smime_keys <operation> [file(s) | keyID [file(s)]]
162162+163163+ with operation being one of:
164164+165165+ init : no files needed, inits directory structure.
166166+167167+ list : lists the certificates stored in database.
168168+ label : keyID required. changes/removes/adds label.
169169+ remove : keyID required.
170170+ verify : 1=keyID and optionally 2=CRL
171171+ Verifies the certificate chain, and optionally wether
172172+ this certificate is included in supplied CRL (PEM format).
173173+ Note: to verify all certificates at the same time,
174174+ replace keyID with "all"
175175+176176+ add_cert : certificate required.
177177+ add_chain : three files reqd: 1=Key, 2=certificate
178178+ plus 3=intermediate certificate(s).
179179+ add_p12 : one file reqd. Adds keypair to database.
180180+ file is PKCS12 (e.g. export from netscape).
181181+ add_pem : one file reqd. Adds keypair to database.
182182+ (file was converted from e.g. PKCS12).
183183+184184+ add_root : one file reqd. Adds PEM root certificate to the location
185185+ specified within muttrc (smime_verify_* command)
186186+187187+EOF
188188+}
189189+190190+sub get_paths ($) {
191191+ my @files = (shift);
192192+ my $certs;
193193+ my $keys;
194194+ my $roots;
195195+ my $switch;
196196+197197+ while (@files) {
198198+ my $file = myglob shift @files;
199199+200200+ if (open(FILE, $file)) {
201201+202202+ while(<FILE>) {
203203+ chomp;
204204+ s/\#.*//;
205205+206206+ /^\s*source\s*\"?([^\"]*)\"?/
207207+ and push @files, $1;
208208+209209+ /^\s*set\s*smime_keys\s*=\s*\"?([^\"]*)\"?/
210210+ and $keys = myglob $1;
211211+212212+ /^\s*set\s*smime_certificates\s*=\s*\"?([^\"]*)\"?/
213213+ and $certs = myglob $1;
214214+215215+ /^\s*set\s*smime_verify[^CA]*(-CA[^\s]*)\s*([^\s]*)./
216216+ and $switch = myglob $1 and $roots = myglob $2;
217217+ }
218218+ close(FILE);
219219+ }
220220+ }
221221+ return ($keys, $certs, $switch, $roots);
222222+}
223223+224224+sub myglob ($) {
225225+ my $file = shift;
226226+227227+ $file =~ s{
228228+ ^ ~ # find a leading tilde
229229+ ( # save this in $1
230230+ [^/] # a non-slash character
231231+ * # repeated 0 or more times (0 means me)
232232+ )
233233+ }{
234234+ $1
235235+ ? (getpwnam($1))[7]
236236+ : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7] )
237237+ }ex;
238238+239239+ return $file;
240240+}
241241+242242+#
243243+# directory setup routines
244244+#
245245+246246+247247+sub mkdir_recursive ($;$) {
248248+ my $path = shift;
249249+ my $mode = 0700;
250250+251251+ (@_ == 2) and $mode = shift;
252252+253253+ my $tmp_path;
254254+255255+ for my $dir (split /\//, $path) {
256256+ $tmp_path .= "$dir/";
257257+258258+ -d $tmp_path
259259+ or mkdir $tmp_path, $mode
260260+ or die "Can't mkdir $tmp_path: $!";
261261+ }
262262+}
263263+264264+sub init_paths () {
265265+ mkdir_recursive($certificates_path);
266266+ mkdir_recursive($private_keys_path);
267267+268268+ my $file;
269269+270270+ $file = $certificates_path . "/.index";
271271+ -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
272272+ or die "Can't touch $file: $!";
273273+274274+ $file = $private_keys_path . "/.index";
275275+ -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
276276+ or die "Can't touch $file: $!";
277277+}
278278+279279+280280+281281+#
282282+# certificate management methods
283283+#
284284+285285+sub list_certs () {
286286+ my %keyflags = ( 'i', '(Invalid)', 'r', '(Revoked)', 'e', '(Expired)',
287287+ 'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)');
288288+289289+ open(INDEX, "<$certificates_path/.index") or
290290+ die "Couldn't open $certificates_path/.index: $!";
291291+292292+ print "\n";
293293+ while(<INDEX>) {
294294+ my $tmp;
295295+ my @tmp;
296296+ my $tab = " ";
297297+ my @fields = split;
298298+299299+ if($fields[2] eq '-') {
300300+ print "$fields[1]: Issued for: $fields[0] $keyflags{$fields[4]}\n";
301301+ } else {
302302+ print "$fields[1]: Issued for: $fields[0] \"$fields[2]\" $keyflags{$fields[4]}\n";
303303+ }
304304+305305+ (my $subject_in, my $email_in, my $issuer_in, my $date1_in, my $date2_in) =
306306+ `openssl x509 -subject -email -issuer -dates -noout -in $certificates_path/$fields[1]`;
307307+308308+ my @subject = split(/\//, $subject_in);
309309+ while(@subject) {
310310+ $tmp = shift @subject;
311311+ ($tmp =~ /^CN\=/) and last;
312312+ undef $tmp;
313313+ }
314314+ defined $tmp and @tmp = split (/\=/, $tmp) and
315315+ print $tab."Subject: $tmp[1]\n";
316316+317317+ my @issuer = split(/\//, $issuer_in);
318318+ while(@issuer) {
319319+ $tmp = shift @issuer;
320320+ ($tmp =~ /^CN\=/) and last;
321321+ undef $tmp;
322322+ }
323323+ defined $tmp and @tmp = split (/\=/, $tmp) and
324324+ print $tab."Issued by: $tmp[1]";
325325+326326+ if ( defined $date1_in and defined $date2_in ) {
327327+ @tmp = split (/\=/, $date1_in);
328328+ $tmp = $tmp[1];
329329+ @tmp = split (/\=/, $date2_in);
330330+ print $tab."Certificate is not valid before $tmp".
331331+ $tab." or after ".$tmp[1];
332332+ }
333333+334334+ -e "$private_keys_path/$fields[1]" and
335335+ print "$tab - Matching private key installed -\n";
336336+337337+ my $purpose_in =
338338+ `openssl x509 -purpose -noout -in $certificates_path/$fields[1]`;
339339+ my @purpose = split (/\n/, $purpose_in);
340340+ print "$tab$purpose[0] (displays S/MIME options only)\n";
341341+ while(@purpose) {
342342+ $tmp = shift @purpose;
343343+ ($tmp =~ /^S\/MIME/ and $tmp =~ /Yes/) or next;
344344+ my @tmptmp = split (/:/, $tmp);
345345+ print "$tab $tmptmp[0]\n";
346346+ }
347347+348348+ print "\n";
349349+ }
350350+351351+ close(INDEX);
352352+}
353353+354354+355355+356356+sub query_label () {
357357+ my @words;
358358+ my $input;
359359+360360+ print "\nYou may assign a label to this key, so you don't have to remember\n";
361361+ print "the key ID. This has to be _one_ word (no whitespaces).\n\n";
362362+363363+ print "Enter label: ";
364364+ chomp($input = <STDIN>);
365365+366366+ my ($label, $junk) = split(/\s/, $input, 2);
367367+368368+ defined $junk
369369+ and print "\nUsing '$label' as label; ignoring '$junk'\n";
370370+371371+ defined $label || ($label = "-");
372372+373373+ return $label;
374374+}
375375+376376+377377+378378+sub add_entry ($$$$;$) {
379379+ my $mailbox = shift;
380380+ my $hashvalue = shift;
381381+ my $use_cert = shift;
382382+ my $label = shift;
383383+ my $issuer_hash = shift;
384384+385385+ my @fields;
386386+387387+ if ($use_cert) {
388388+ open(INDEX, "+<$certificates_path/.index") or
389389+ die "Couldn't open $certificates_path/.index: $!";
390390+ }
391391+ else {
392392+ open(INDEX, "+<$private_keys_path/.index") or
393393+ die "Couldn't open $private_keys_path/.index: $!";
394394+ }
395395+396396+ while(<INDEX>) {
397397+ @fields = split;
398398+ return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue);
399399+ }
400400+401401+ if ($use_cert) {
402402+ print INDEX "$mailbox $hashvalue $label $issuer_hash u\n";
403403+ }
404404+ else {
405405+ print INDEX "$mailbox $hashvalue $label \n";
406406+ }
407407+408408+ close(INDEX);
409409+}
410410+411411+412412+sub add_certificate ($$$$;$) {
413413+ my $filename = shift;
414414+ my $hashvalue = shift;
415415+ my $add_to_index = shift;
416416+ my $label = shift;
417417+ my $issuer_hash = shift;
418418+419419+ my $iter = 0;
420420+ my $mailbox;
421421+422422+ while(-e "$certificates_path/$$hashvalue.$iter") {
423423+ my ($t1, $t2);
424424+425425+ my $cmd = "openssl x509 -in $filename -fingerprint -noout";
426426+ $t1 = `$cmd`;
427427+ $? and die "'$cmd' returned $?";
428428+429429+ $cmd = "openssl x509 -in $certificates_path/$$hashvalue.$iter -fingerprint -noout";
430430+ $t2 = `$cmd`;
431431+ $? and die "'$cmd' returned $?";
432432+433433+ $t1 eq $t2 and last;
434434+435435+ $iter++;
436436+ }
437437+ $$hashvalue .= ".$iter";
438438+439439+ unless (-e "$certificates_path/$$hashvalue") {
440440+ my $cmd = "cp $filename $certificates_path/$$hashvalue";
441441+ system $cmd and die "'$cmd' returned $?";
442442+443443+ if ($add_to_index) {
444444+ my $cmd = "openssl x509 -in $filename -email -noout";
445445+ $mailbox = `$cmd`;
446446+ $? and die "'$cmd' returned $?";
447447+448448+ chomp($mailbox);
449449+ add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
450450+451451+ print "added certificate: $certificates_path/$$hashvalue for $mailbox.\n";
452452+ }
453453+ else {
454454+ print "added certificate: $certificates_path/$$hashvalue.\n";
455455+ }
456456+ }
457457+458458+ return $mailbox;
459459+}
460460+461461+462462+sub add_key ($$$$) {
463463+ my $file = shift;
464464+ my $hashvalue = shift;
465465+ my $mailbox = shift;
466466+ my $label = shift;
467467+468468+ unless (-e "$private_keys_path/$hashvalue") {
469469+ my $cmd = "cp $file $private_keys_path/$hashvalue";
470470+ system $cmd and die "$cmd returned $!";
471471+ print "added private key: " .
472472+ "$private_keys_path/$hashvalue for $mailbox\n";
473473+ add_entry($mailbox, $hashvalue, 0, $label, "");
474474+ }
475475+}
476476+477477+478478+479479+480480+481481+482482+sub parse_pem (@) {
483483+ my $state = 0;
484484+ my $cert_iter = 0;
485485+ my @bag_attribs;
486486+ my $numBags = 0;
487487+488488+ open(CERT_FILE, ">cert_tmp.$cert_iter")
489489+ or die "Couldn't open cert_tmp.$cert_iter: $!";
490490+491491+ while($_ = shift(@_)) {
492492+ if(/^Bag Attributes/) {
493493+ $numBags++;
494494+ $state == 0 or die("PEM-parse error at: $.");
495495+ $state = 1;
496496+ $bag_attribs[$cert_iter*4+1] = "";
497497+ $bag_attribs[$cert_iter*4+2] = "";
498498+ $bag_attribs[$cert_iter*4+3] = "";
499499+ }
500500+501501+ ($state == 1) and /localKeyID:\s*(.*)/
502502+ and ($bag_attribs[$cert_iter*4+1] = $1);
503503+504504+ ($state == 1) and /subject=\s*(.*)/
505505+ and ($bag_attribs[$cert_iter*4+2] = $1);
506506+507507+ ($state == 1) and /issuer=\s*(.*)/
508508+ and ($bag_attribs[$cert_iter*4+3] = $1);
509509+510510+ if(/^-----/) {
511511+ if(/BEGIN/) {
512512+ print CERT_FILE;
513513+ $state = 2;
514514+515515+ if(/PRIVATE/) {
516516+ $bag_attribs[$cert_iter*4] = "K";
517517+ next;
518518+ }
519519+ if(/CERTIFICATE/) {
520520+ $bag_attribs[$cert_iter*4] = "C";
521521+ next;
522522+ }
523523+ die("What's this: $_");
524524+ }
525525+ if(/END/) {
526526+ $state = 0;
527527+ print CERT_FILE;
528528+ close(CERT_FILE);
529529+ $cert_iter++;
530530+ open(CERT_FILE, ">cert_tmp.$cert_iter")
531531+ or die "Couldn't open cert_tmp.$cert_iter: $!";
532532+ next;
533533+ }
534534+ }
535535+ print CERT_FILE;
536536+ }
537537+ close(CERT_FILE);
538538+539539+ # I'll add support for unbagged cetificates, in case this is needed.
540540+ $numBags == $cert_iter or
541541+ die("Not all contents were bagged. can't continue.");
542542+543543+ @bag_attribs;
544544+}
545545+546546+547547+# This requires the Bag Attributes to be set
548548+sub handle_pem (@) {
549549+550550+ my @pem_contents;
551551+ my $iter=0;
552552+ my $root_cert;
553553+ my $key;
554554+ my $certificate;
555555+ my $mailbox;
556556+557557+ @pem_contents = &parse_pem(@_);
558558+559559+ # private key and certificate use the same 'localKeyID'
560560+ while($iter <= $#pem_contents>>2) {
561561+ if($pem_contents[$iter<<2] eq "K") {
562562+ $key = $iter;
563563+ last;
564564+ }
565565+ $iter++;
566566+ }
567567+ ($key > $#pem_contents>>2) and die("Couldn't find private key!");
568568+569569+ $pem_contents[($key<<2)+1] or die("Attribute 'localKeyID' wasn't set.");
570570+571571+ $iter = 0;
572572+ while($iter <= $#pem_contents>>2) {
573573+ $iter == $key and ($iter++) and next;
574574+ if($pem_contents[($iter<<2)+1] eq $pem_contents[($key<<2)+1]) {
575575+ $certificate = $iter;
576576+ last;
577577+ }
578578+ $iter++;
579579+ }
580580+ ($certificate > $#pem_contents>>2) and die("Couldn't find matching certificate!");
581581+582582+ my $cmd = "cp cert_tmp.$key tmp_key";
583583+ system $cmd and die "'$cmd' returned $?";
584584+585585+ $cmd = "cp cert_tmp.$certificate tmp_certificate";
586586+ system $cmd and die "'$cmd' returned $?";
587587+588588+ # root certificate is self signed
589589+ $iter = 0;
590590+591591+ while($iter <= $#pem_contents>>2) {
592592+ if ($iter == $key or $iter == $certificate) {
593593+ $iter++;
594594+ next;
595595+ }
596596+597597+ if($pem_contents[($iter<<2)+2] eq $pem_contents[($iter<<2)+3]) {
598598+ $root_cert = $iter;
599599+ last;
600600+ }
601601+ $iter++;
602602+ }
603603+ ($root_cert > $#pem_contents>>2) and die("Couldn't identify root certificate!");
604604+605605+ # what's left are intermediate certificates.
606606+ $iter = 0;
607607+608608+ $cmd = "rm -f tmp_issuer_cert";
609609+ system $cmd and die "'$cmd' returned $?";
610610+611611+ while($iter <= $#pem_contents>>2) {
612612+ if ($iter == $key or $iter == $certificate or $iter == $root_cert) {
613613+ $iter++;
614614+ next;
615615+ }
616616+617617+ my $cmd = "cat cert_tmp.$iter >> tmp_issuer_cert";
618618+ system $cmd and die "'$cmd' returned $?";
619619+620620+ $iter++;
621621+ }
622622+623623+ my $label = query_label;
624624+625625+ $cmd = "openssl x509 -noout -hash -in tmp_certificate";
626626+ my $cert_hash = `$cmd`;
627627+ $? and die "'$cmd' returned $?";
628628+629629+ $cmd = "openssl x509 -noout -hash -in tmp_issuer_cert";
630630+ my $issuer_hash = `$cmd`;
631631+ $? and die "'$cmd' returned $?";
632632+633633+ chomp($cert_hash); chomp($issuer_hash);
634634+635635+ # Note: $cert_hash will be changed to reflect the correct filename
636636+ # within add_cert() ONLY, so these _have_ to get called first..
637637+ add_certificate("tmp_issuer_cert", \$issuer_hash, 0, $label);
638638+ $mailbox = &add_certificate("tmp_certificate", \$cert_hash, 1, $label, $issuer_hash);
639639+ add_key("tmp_key", $cert_hash, $mailbox, $label);
640640+641641+ $cmd = "rm -f cert_tmp.* tmp_*";
642642+ system $cmd and die "'$cmd' returned $?";
643643+}
644644+645645+646646+647647+648648+649649+650650+sub modify_entry ($$$;$ ) {
651651+ my $op = shift;
652652+ my $hashvalue = shift;
653653+ my $use_cert = shift;
654654+ my $crl;
655655+ my $label;
656656+ my $path;
657657+ my @fields;
658658+659659+ $op eq 'L' and ($label = shift);
660660+ $op eq 'V' and ($crl = shift);
661661+662662+663663+ if ($use_cert) {
664664+ $path = $certificates_path;
665665+ }
666666+ else {
667667+ $path = $private_keys_path;
668668+ }
669669+670670+ open(INDEX, "<$path/.index") or
671671+ die "Couldn't open $path/.index: $!";
672672+ open(NEW_INDEX, ">$path/.index.tmp") or
673673+ die "Couldn't create $path/.index.tmp: $!";
674674+675675+ while(<INDEX>) {
676676+ @fields = split;
677677+ if($fields[1] eq $hashvalue or $hashvalue eq 'all') {
678678+ $op eq 'R' and next;
679679+ print NEW_INDEX "$fields[0] $fields[1]";
680680+ if($op eq 'L') {
681681+ if($use_cert) {
682682+ print NEW_INDEX " $label $fields[3] $fields[4]";
683683+ }
684684+ else {
685685+ print NEW_INDEX " $label";
686686+ }
687687+ }
688688+ if ($op eq 'V') {
689689+ print "\n==> about to verify certificate of $fields[0]\n";
690690+ my $flag = &do_verify($fields[1], $fields[3], $crl);
691691+ print NEW_INDEX " $fields[2] $fields[3] $flag";
692692+ }
693693+ print NEW_INDEX "\n";
694694+ next;
695695+ }
696696+ print NEW_INDEX;
697697+ }
698698+ close(INDEX);
699699+ close(NEW_INDEX);
700700+701701+ my $cmd = "mv -f $path/.index.tmp $path/.index";
702702+ system $cmd and die "'$cmd' returned $?";
703703+704704+ print "\n";
705705+}
706706+707707+708708+709709+710710+sub remove_pair ($ ) {
711711+ my $keyid = shift;
712712+713713+ if (-e "$certificates_path/$keyid") {
714714+ my $cmd = "rm -f $certificates_path/$keyid";
715715+ system $cmd and die "'$cmd' returned $?";
716716+ modify_entry('R', $keyid, 1);
717717+ print "Removed certificate $keyid.\n";
718718+ }
719719+ else {
720720+ die "No such certificate: $keyid";
721721+ }
722722+723723+ if (-e "$private_keys_path/$keyid") {
724724+ my $cmd = "rm -f $private_keys_path/$keyid";
725725+ system $cmd and die "'$cmd' returned $?";
726726+ modify_entry('R', $keyid, 0);
727727+ print "Removed private key $keyid.\n";
728728+ }
729729+}
730730+731731+732732+733733+sub change_label ($ ) {
734734+ my $keyid = shift;
735735+736736+ my $label = query_label;
737737+738738+ if (-e "$certificates_path/$keyid") {
739739+ modify_entry('L', $keyid, 1, $label);
740740+ print "Changed label for certificate $keyid.\n";
741741+ }
742742+ else {
743743+ die "No such certificate: $keyid";
744744+ }
745745+746746+ if (-e "$private_keys_path/$keyid") {
747747+ modify_entry('L', $keyid, 0, $label);
748748+ print "Changed label for private key $keyid.\n";
749749+ }
750750+751751+}
752752+753753+754754+755755+756756+sub verify_cert ($;$ ) {
757757+ my $keyid = shift;
758758+ my $crl = shift;
759759+760760+ -e "$certificates_path/$keyid" or $keyid eq 'all'
761761+ or die "No such certificate: $keyid";
762762+ modify_entry('V', $keyid, 1, $crl);
763763+}
764764+765765+766766+767767+768768+sub do_verify($$;$) {
769769+770770+ my $cert = shift;
771771+ my $issuerid = shift;
772772+ my $crl = shift;
773773+774774+ my $result = 'i';
775775+ my $trust_q;
776776+ my $issuer_path;
777777+ my $cert_path = "$certificates_path/$cert";
778778+779779+ if($issuerid eq '?') {
780780+ $issuer_path = "$certificates_path/$cert";
781781+ } else {
782782+ $issuer_path = "$certificates_path/$issuerid";
783783+ }
784784+785785+ my $output = `openssl verify $root_certs_switch $root_certs_path -purpose smimesign -purpose smimeencrypt -untrusted $issuer_path $cert_path`;
786786+ chop $output;
787787+ print "\n$output\n";
788788+789789+ ($output =~ /OK/) and ($result = 'v');
790790+791791+ $result eq 'i' and return $result;
792792+793793+794794+ (my $date1_in, my $date2_in, my $serial_in) =
795795+ `openssl x509 -dates -serial -noout -in $cert_path`;
796796+797797+ if ( defined $date1_in and defined $date2_in ) {
798798+ my @tmp = split (/\=/, $date1_in);
799799+ my $tmp = $tmp[1];
800800+ @tmp = split (/\=/, $date2_in);
801801+ my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03',
802802+ 'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07',
803803+ 'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11');
804804+805805+ my @fields =
806806+ $tmp =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
807807+808808+ $#fields != 5 and print "Expiration Date: Parse Error : $tmp\n\n" or
809809+ timegm($fields[4], $fields[3], $fields[2], $fields[1],
810810+ $months{$fields[0]}, $fields[5]) > time and $result = 'e';
811811+ $result eq 'e' and print "Certificate is not yet valid.\n" and return $result;
812812+813813+ @fields =
814814+ $tmp[1] =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
815815+816816+ $#fields != 5 and print "Expiration Date: Parse Error : $tmp[1]\n\n" or
817817+ timegm($fields[4], $fields[3], $fields[2], $fields[1],
818818+ $months{$fields[0]}, $fields[5]) < time and $result = 'e';
819819+ $result eq 'e' and print "Certificate has expired.\n" and return $result;
820820+821821+ }
822822+823823+ if ( defined $crl ) {
824824+ my @serial = split (/\=/, $serial_in);
825825+ (my $l1, my $l2) =
826826+ `openssl crl -text -noout -in $crl |grep -A1 $serial[1]`;
827827+828828+ if ( defined $l2 ) {
829829+ my @revoke_date = split (/:\s/, $l2);
830830+ print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n";
831831+ $result = 'r';
832832+ }
833833+ }
834834+ print "\n";
835835+836836+ if ($result eq 'v') {
837837+ print "Certificate was successfully verified.\nDo you choose to trust this certificate ? (yes/no) ";
838838+ chomp($trust_q = <STDIN>);
839839+ $trust_q eq 'yes' and $result = 't';
840840+ }
841841+842842+ return $result;
843843+}
844844+845845+846846+847847+sub add_root_cert ($) {
848848+ my $root_cert = shift;
849849+850850+ my $cmd = "openssl x509 -noout -hash -in $root_cert";
851851+ my $root_hash = `$cmd`;
852852+ $? and die "'$cmd' returned $?";
853853+854854+ if (-d $root_certs_path) {
855855+ $cmd = "cp $root_cert $root_certs_path/$root_hash";
856856+ -e "$root_certs_path/$root_hash" or
857857+ system $cmd and die "'$cmd' returned $?";
858858+ }
859859+ else {
860860+ open(ROOT_CERTS, ">>$root_certs_path") or
861861+ die ("Couldn't open $root_certs_path for writing");
862862+863863+ $cmd = "openssl x509 -in $root_cert -fingerprint -noout";
864864+ $? and die "'$cmd' returned $?";
865865+ chomp(my $md5fp = `$cmd`);
866866+867867+ $cmd = "openssl x509 -in $root_cert -text -noout";
868868+ $? and die "'$cmd' returned $?";
869869+ my @cert_text = `$cmd`;
870870+871871+ print "Enter a label, name or description for this certificate: ";
872872+ my $input = <STDIN>;
873873+874874+ my $line = "=======================================\n";
875875+ print ROOT_CERTS "\n$input$line$md5fp\nPEM-Data:\n";
876876+877877+ open(IN_CERT, "<$root_cert");
878878+ while (<IN_CERT>) {
879879+ print ROOT_CERTS;
880880+ }
881881+ close (IN_CERT);
882882+ print ROOT_CERTS @cert_text;
883883+ close (ROOT_CERTS);
884884+ }
885885+886886+}
887887+