summaryrefslogtreecommitdiff
path: root/contrib/smime/pine-smime-20030115.diff
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/smime/pine-smime-20030115.diff')
-rw-r--r--contrib/smime/pine-smime-20030115.diff3265
1 files changed, 3265 insertions, 0 deletions
diff --git a/contrib/smime/pine-smime-20030115.diff b/contrib/smime/pine-smime-20030115.diff
new file mode 100644
index 00000000..5744ce0d
--- /dev/null
+++ b/contrib/smime/pine-smime-20030115.diff
@@ -0,0 +1,3265 @@
+diff -Ncr pine4.53/imap/src/c-client/mail.c pine4.53-smime/imap/src/c-client/mail.c
+*** pine4.53/imap/src/c-client/mail.c Thu Jan 2 17:28:42 2003
+--- pine4.53-smime/imap/src/c-client/mail.c Wed Jan 15 12:48:15 2003
+***************
+*** 5198,5203 ****
+--- 5198,5207 ----
+
+ void mail_free_body_data (BODY *body)
+ {
++ /* cleanup body if requested by application */
++ if (body->cleanup)
++ (*body->cleanup)(body);
++
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ mail_free_body_part (&body->nested.part);
+diff -Ncr pine4.53/imap/src/c-client/mail.h pine4.53-smime/imap/src/c-client/mail.h
+*** pine4.53/imap/src/c-client/mail.h Tue Jan 7 12:33:50 2003
+--- pine4.53-smime/imap/src/c-client/mail.h Wed Jan 15 12:48:15 2003
+***************
+*** 655,660 ****
+--- 655,663 ----
+ unsigned long bytes; /* size of text in octets */
+ } size;
+ char *md5; /* MD5 checksum */
++
++ void *sparep; /* spare pointer reserved for main program */
++ void (*cleanup)(BODY *); /* cleanup function */
+ };
+
+
+diff -Ncr pine4.53/pine/README.smime pine4.53-smime/pine/README.smime
+*** pine4.53/pine/README.smime Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/README.smime Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,129 ----
++ Quick Start
++ ===========
++
++ To enable S/MIME support, ensure the SSL related lines in the platform
++ makefile are uncommented (they're all next to one another).
++
++ CA certificates are expected to be found in the OpenSSL 'cert' dir, in the
++ standard hashed format.
++
++ User Configuration
++ ==================
++
++ A directory '~/.pine-smime' must be created. Within this, a further
++ three directories are required:
++
++ ~/.pine-smime/
++ ca/
++ private/
++ public/
++
++ Other people's certificate files should be copied into the 'public' directory.
++ Your certificate file(s) should be copied into the 'private' directory.
++ Certificates for any additional trusted CAs should be put in the 'ca' directory.
++
++ There are three extra configuration options:
++
++ sign-default-on
++ encrypt-default-on
++ remember-smime-passphrase
++
++ Certificates
++ ============
++
++ The certificate files specified above should have the following form:
++
++ public certificates: user@emaildomain.crt
++ private keys: user@emaildomain.key
++
++ Thus, a typical installation might look like this:
++
++ ~/.pine-smime/
++ ca/
++ [additional trusted CAs here]
++ private/
++ paisleyj@dcs.gla.ac.uk.crt
++ paisleyj@dcs.gla.ac.uk.key
++ public/
++ myfriend@dcs.gla.ac.uk.crt
++ myotherfriend@dcs.gla.ac.uk.crt
++
++ Implementation Details
++ ======================
++
++ Link with the OpenSSL crypto library for PKCS7 support.
++ Only tested on linux (slx) and solaris (so5).
++
++ Added three extra source files (+headers):
++
++ smime.c Main S/MIME support code
++ smkeys.c Very basic X509 key handling/storage (using the above dirs)
++ bss_so.c OpenSSL BIO using pine STORE_S objects
++
++ Patches to existing pine sources:
++
++ init.c
++ Add references to new configuration options.
++
++ mailcmd.c
++ Add implementation of MC_DECRYPT command which prompts
++ the user for a passphrase if it's required.
++
++ mailpart.c
++ Comment added to help me remember what I'd done.
++
++ mailview.c
++ Added description of Decrypt menu option.
++ Make calls out to smime.c functions to handle the decryption.
++ This is done shortly after the BODY of a message is
++ obtained.
++ Added function to describe encrypted messages when they're
++ being displayed.
++ Added code to describe the special case of PKCS7 attachments.
++
++ makefile.lnx
++ makefile.so5
++ Added SSL variables etc.
++
++ pine.h
++ Add enumerations for new configuration options and definition
++ of MC_DECRYPT command
++ Exported the prototype of pine_write_body_header,
++ pine_rfc822_output_body and pine_encode_body since they're
++ needed in smime.c.
++
++ pine.hlp
++ Added help info for new configuration options.
++
++ send.c
++ Added 'Encrypt' and 'Sign' menu options when sending email.
++ Make calls to smime.c functions to fiddle message on the
++ way out.
++ Extend pine_encode_body so it makes a few more checks
++ before adding a boundary.
++
++ Basic method:
++
++ Incoming
++
++ Scan BODY of viewed message before it is formatted. If it contains
++ PKCS7 parts, decode them and attempt to decrypt/verify signatures. The
++ original BODY is fiddled in place and turned into a multipart body
++ with one subpart -- the decrypted data. This may consist of a multipart
++ with attachments, for example.
++
++ This all depends on stashing a pointer to the decrypted data in
++ body->contents.text.data and relying on the fact that the mail_* routines
++ will use this data in preference to fetching it over the network. We
++ also depend on it not being garbage collected while the message is
++ being viewed!
++
++ Outgoing
++
++ smime.c pre-builds the message using pine_encode_body, pine_write_body_header
++ and pine_rfc822_output_body, encrypting/signing the resulting data. The
++ body that was going to be sent is then fiddled appropriately after
++ the PKCS7 objects have been built.
++
++ paisleyj@dcs.gla.ac.uk
++ Mar 7 2001
+diff -Ncr pine4.53/pine/TODO.smime pine4.53-smime/pine/TODO.smime
+*** pine4.53/pine/TODO.smime Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/TODO.smime Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,31 ----
++
++ Need to be able to view stored certificates to see details
++ (particularly the fingerprint for comparing over the phone, say)
++ --> proper key management system
++
++ Add client private key and certificate request generation.
++
++ Send certificate for CA along with certificate of signer.
++
++ Verify recipient certificate before sending encrypted message.
++
++ Verify certificates in general.
++
++ Cache the result of pre-formatting the message during the send/encrypt/sign
++ phase rather than letting call_mailer re-format it all over again.
++
++ Tidy up the use of global variables considerably.
++
++ Intelligently pick a certificate for signing purposes based on the
++ From address rather than just picking the first one on the list.
++
++ Figure out platform dependancies from using readdir() in smkeys.c
++
++ Handle message/rfc822 sub-parts!
++
++ Consider what happens with all our cached data.
++
++ S/MIME info screen help.
++
++ paisleyj@dcs.gla.ac.uk
++ Mar 7 2001
+diff -Ncr pine4.53/pine/bss_so.c pine4.53-smime/pine/bss_so.c
+*** pine4.53/pine/bss_so.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/bss_so.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,184 ----
++ #ifdef SMIME
++
++ /*
++ bss_so.c
++
++ Basic implementation of an OpenSSL BIO which is
++ backed by a pine STORE_S object
++ */
++
++ #include "headers.h"
++
++ #include <stdio.h>
++ #include <errno.h>
++ #include <openssl/bio.h>
++ #include <openssl/err.h>
++
++ static int bss_so_write(BIO *h, char *buf, int num);
++ static int bss_so_read(BIO *h, char *buf, int size);
++ static int bss_so_puts(BIO *h, char *str);
++ static int bss_so_gets(BIO *h, char *str, int size);
++ static long bss_so_ctrl(BIO *h, int cmd, long arg1, char *arg2);
++ static int bss_so_new(BIO *h);
++ static int bss_so_free(BIO *data);
++ static BIO_METHOD methods_sop =
++ {
++ BIO_TYPE_MEM,
++ "Storage Object",
++ bss_so_write,
++ bss_so_read,
++ bss_so_puts,
++ bss_so_gets,
++ bss_so_ctrl,
++ bss_so_new,
++ bss_so_free,
++ NULL,
++ };
++
++ BIO *BIO_new_so(STORE_S *so)
++ {
++ BIO *ret;
++
++ if ((ret = BIO_new(&methods_sop)) == NULL)
++ return (NULL);
++
++ BIO_set_fp(ret, (FILE*) so, 0);
++ return (ret);
++ }
++
++
++ static int bss_so_new(BIO *bi)
++ {
++ bi->init = 0;
++ bi->num = 0;
++ bi->ptr = NULL;
++ return (1);
++ }
++
++ static int bss_so_free(BIO *a)
++ {
++ if (a == NULL) return (0);
++ if (a->shutdown) {
++ if ((a->init) && (a->ptr != NULL)) {
++ a->ptr = NULL;
++ }
++ a->init = 0;
++ }
++ return (1);
++ }
++
++ static int bss_so_read(BIO *b, char *out, int outl)
++ {
++ int ret = 0;
++ STORE_S *so = (STORE_S*) b->ptr;
++
++ if (b->init && (out != NULL)) {
++
++ while (ret < outl) {
++ if (!so->readc((unsigned char *)out, so))
++ break;
++ out++;
++ ret++;
++ }
++
++ }
++ return (ret);
++ }
++
++ static int bss_so_write(BIO *b, char *in, int inl)
++ {
++ int ret = 0;
++
++ if (b->init && (in != NULL)) {
++ if (so_nputs((STORE_S *)b->ptr, in, inl))
++ ret = inl;
++
++ }
++ return (ret);
++ }
++
++ static long bss_so_ctrl(BIO *b, int cmd, long num, char *ptr)
++ {
++ long ret = 1;
++ STORE_S *so = (STORE_S *)b->ptr;
++ FILE **fpp;
++ char p[4];
++
++ switch (cmd) {
++ case BIO_C_FILE_SEEK:
++ case BIO_CTRL_RESET:
++ ret = so_seek(so, num, 0);
++ break;
++ case BIO_CTRL_EOF:
++ ret = 0;
++ break;
++ case BIO_C_FILE_TELL:
++ case BIO_CTRL_INFO:
++ ret = 0;
++ break;
++ case BIO_C_SET_FILE_PTR:
++ bss_so_free(b);
++ b->shutdown = (int)num & BIO_CLOSE;
++ b->ptr = (char *)ptr;
++ b->init = 1;
++ break;
++ case BIO_C_SET_FILENAME:
++ ret = 0;
++ break;
++ case BIO_C_GET_FILE_PTR:
++ if (ptr != NULL) {
++ fpp = (FILE **)ptr;
++ *fpp = (FILE *)NULL;
++ }
++ break;
++ case BIO_CTRL_GET_CLOSE:
++ ret = (long)b->shutdown;
++ break;
++ case BIO_CTRL_SET_CLOSE:
++ b->shutdown = (int)num;
++ break;
++ case BIO_CTRL_FLUSH:
++ break;
++ case BIO_CTRL_DUP:
++ ret = 1;
++ break;
++
++ case BIO_CTRL_WPENDING:
++ case BIO_CTRL_PENDING:
++ case BIO_CTRL_PUSH:
++ case BIO_CTRL_POP:
++ default:
++ ret = 0;
++ break;
++ }
++ return (ret);
++ }
++
++ static int bss_so_gets(BIO *bp, char *buf, int size)
++ {
++ int ret = 0;
++ char *b = buf;
++ char *bend = buf + size - 1;
++ STORE_S *so = (STORE_S*) bp->ptr;
++
++ do {
++ if (!so->readc((unsigned char *)b, so))
++ break;
++ b++;
++ } while (b < bend && b[ -1] != '\n');
++
++ *b = 0;
++
++ ret = b - buf;
++ return (ret);
++ }
++
++ static int bss_so_puts(BIO *bp, char *str)
++ {
++ STORE_S *so = (STORE_S*) bp->ptr;
++
++ return so->puts(so, str);
++ }
++
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/bss_so.h pine4.53-smime/pine/bss_so.h
+*** pine4.53/pine/bss_so.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/bss_so.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1 ----
++ BIO *BIO_new_so(STORE_S *so);
+diff -Ncr pine4.53/pine/filter.c pine4.53-smime/pine/filter.c
+*** pine4.53/pine/filter.c Wed Jan 8 12:13:50 2003
+--- pine4.53-smime/pine/filter.c Wed Jan 15 12:48:15 2003
+***************
+*** 903,909 ****
+
+
+ /* get a character from a file */
+! /* assumes gf_out struct is filled in */
+ int
+ gf_freadc(c)
+ unsigned char *c;
+--- 903,909 ----
+
+
+ /* get a character from a file */
+! /* assumes gf_in struct is filled in */
+ int
+ gf_freadc(c)
+ unsigned char *c;
+***************
+*** 938,944 ****
+
+
+ /* get a character from a string, return nonzero if things OK */
+! /* assumes gf_out struct is filled in */
+ int
+ gf_sreadc(c)
+ unsigned char *c;
+--- 938,944 ----
+
+
+ /* get a character from a string, return nonzero if things OK */
+! /* assumes gf_in struct is filled in */
+ int
+ gf_sreadc(c)
+ unsigned char *c;
+diff -Ncr pine4.53/pine/init.c pine4.53-smime/pine/init.c
+*** pine4.53/pine/init.c Mon Jan 6 19:39:16 2003
+--- pine4.53-smime/pine/init.c Wed Jan 15 12:48:15 2003
+***************
+*** 2445,2451 ****
+ F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
+ {"use-sender-not-x-sender",
+ F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
+!
+ /* Folder */
+ {"combined-subdirectory-display",
+ F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
+--- 2445,2458 ----
+ F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
+ {"use-sender-not-x-sender",
+ F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
+! #ifdef SMIME
+! {"sign-default-on",
+! F_SIGN_DEFAULT_ON, h_config_sign_default_on, PREF_SEND},
+! {"encrypt-default-on",
+! F_ENCRYPT_DEFAULT_ON, h_config_encrypt_default_on, PREF_SEND},
+! {"remember-smime-passphrase",
+! F_REMEMBER_SMIME_PASSPHRASE, h_config_remember_smime_passphrase, PREF_SEND},
+! #endif
+ /* Folder */
+ {"combined-subdirectory-display",
+ F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
+diff -Ncr pine4.53/pine/mailcmd.c pine4.53-smime/pine/mailcmd.c
+*** pine4.53/pine/mailcmd.c Fri Jan 10 15:29:29 2003
+--- pine4.53-smime/pine/mailcmd.c Wed Jan 15 12:48:15 2003
+***************
+*** 53,58 ****
+--- 53,59 ----
+ #include "headers.h"
+ #include "../c-client/imap4r1.h"
+
++ #include "smime.h"
+
+ /*
+ * Internal Prototypes
+***************
+*** 1439,1444 ****
+--- 1440,1458 ----
+ break;
+
+
++ #ifdef SMIME
++ /*------- Try to decrypt message -----------*/
++ case MC_DECRYPT:
++ if (g_need_passphrase)
++ get_passphrase();
++ a_changed = TRUE;
++ break;
++
++ case MC_SECURITY:
++ state->next_screen = smime_info_screen;
++ break;
++ #endif
++
+ /*------- Bounce -----------*/
+ case MC_BOUNCE :
+ cmd_bounce(state, msgmap, 0);
+diff -Ncr pine4.53/pine/mailpart.c pine4.53-smime/pine/mailpart.c
+*** pine4.53/pine/mailpart.c Wed Nov 27 15:22:54 2002
+--- pine4.53-smime/pine/mailpart.c Wed Jan 15 12:48:15 2003
+***************
+*** 4534,4539 ****
+--- 4534,4545 ----
+ frd->flags = flags;
+ frd->size = size;
+ frd->readc = fetch_readc;
++
++ /* The call to imap_cache below will return true in the case where
++ we've already stashed fake data in the content of the part.
++ This happens when an S/MIME message is decrypted.
++ */
++
+ if(modern_imap_stream(stream)
+ && !imap_cache(stream, msgno, section, NULL, NULL)
+ && size > INIT_FETCH_CHUNK
+diff -Ncr pine4.53/pine/mailview.c pine4.53-smime/pine/mailview.c
+*** pine4.53/pine/mailview.c Tue Jan 7 16:17:00 2003
+--- pine4.53-smime/pine/mailview.c Wed Jan 15 12:48:15 2003
+***************
+*** 47,52 ****
+--- 47,53 ----
+
+
+ #include "headers.h"
++ #include "smime.h"
+
+
+ /*----------------------------------------------------------------------
+***************
+*** 207,214 ****
+--- 208,220 ----
+
+ HELP_MENU,
+ OTHER_MENU,
++ #ifdef SMIME
++ {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}},
++ {"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}},
++ #else
+ NULL_MENU,
+ NULL_MENU,
++ #endif
+ RCOMPOSE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+***************
+*** 227,232 ****
+--- 233,240 ----
+ #define BOUNCE_KEY 33
+ #define FLAG_KEY 34
+ #define VIEW_PIPE_KEY 35
++ #define DECRYPT_KEY (VIEW_PIPE_KEY + 3)
++ #define SECURITY_KEY (DECRYPT_KEY + 1)
+
+ static struct key simple_text_keys[] =
+ {HELP_MENU,
+***************
+*** 432,437 ****
+--- 440,447 ----
+ else
+ ps->unseen_in_view = !mc->seen;
+
++ flags = 0;
++
+ #if defined(DOS) && !defined(WIN32)
+ /*
+ * Handle big text for DOS here.
+***************
+*** 459,465 ****
+ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
+ + SCROLL_LINES_BELOW(ps)));
+
+! flags = FM_DISPLAY;
+ if((last_message_viewed != mn_get_cur(ps->msgmap)
+ || last_was_full_header == 1))
+ flags |= FM_NEW_MESS;
+--- 469,475 ----
+ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
+ + SCROLL_LINES_BELOW(ps)));
+
+! flags |= FM_DISPLAY;
+ if((last_message_viewed != mn_get_cur(ps->msgmap)
+ || last_was_full_header == 1))
+ flags |= FM_NEW_MESS;
+***************
+*** 467,472 ****
+--- 477,488 ----
+ if(offset) /* no pre-paint during resize */
+ view_writec_killbuf();
+
++ #ifdef SMIME
++ /* Attempt to handle S/MIME bodies */
++ if (fiddle_smime_message(body,raw_msgno,(flags&FM_NEW_MESS)!=0))
++ flags |= FM_NEW_MESS; /* body was changed, force a reload */
++ #endif
++
+ #ifdef _WINDOWS
+ mswin_noscrollupdate(1);
+ #endif
+***************
+*** 541,546 ****
+--- 557,567 ----
+
+ if(F_OFF(F_ENABLE_FULL_HDR, ps_global))
+ clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap);
++
++ #ifdef SMIME
++ if (!g_need_passphrase)
++ clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
++ #endif
+
+ if(!handles){
+ /*
+***************
+*** 754,760 ****
+--- 775,821 ----
+ }
+
+
++ #ifdef SMIME
++ /*----------------------------------------------------------------------
++ Add descriptive lines to the top of a message being formatted
++ that describe the status of any S/MIME enclosures that
++ have been encountered.
++
++ Args: body -- top-level body of the message being described
++ pc -- output function for writing to the message display
++
++ ----*/
++ static int describe_smime_bodies(BODY *body,gf_io_t pc)
++ {
++ PART *part;
++ int result = 0;
++
++ if (!body)
++ return result;
++
++ if (body->type == TYPEMULTIPART) {
++
++ if (body->subtype && strucmp(body->subtype,"x-pkcs7-enclosure")==0) {
++
++ if (body->description) {
++ format_editorial(body->description,ps_global->ttyo->screen_cols,pc);
++ gf_puts(NEWLINE,pc);
++ result = 1;
++ }
+
++ for (part=body->nested.part; part; part=part->next) {
++ result |= describe_smime_bodies(&(part->body),pc);
++ }
++
++ }
++ } else if (body->type == TYPEMESSAGE &&
++ body->subtype && strucmp(body->subtype, "rfc822")==0) {
++ result |= describe_smime_bodies(body->nested.msg->body,pc);
++ }
++
++ return result;
++ }
++ #endif
+
+ /*----------------------------------------------------------------------
+ Add lines to the attachments structure
+***************
+*** 1677,1682 ****
+--- 1738,1751 ----
+
+ show_parts = 0;
+
++ #ifdef SMIME
++ if (flgs & FM_DISPLAY) {
++ if (describe_smime_bodies(body,pc)) {
++ gf_puts(NEWLINE, pc);
++ }
++ }
++ #endif
++
+ /*======== Now loop through formatting all the parts =======*/
+ for(a = ps_global->atmts; a->description != NULL; a++) {
+
+***************
+*** 6150,6155 ****
+--- 6219,6236 ----
+
+ t = &tmp_20k_buf[strlen(tmp_20k_buf)];
+
++ #ifdef SMIME
++ if (is_pkcs7_body(body) && type!=3) { /* if smime and not attempting print */
++
++ sstrcpy(&t,"\015\012");
++
++ sstrcpy(&t,
++ "This part is a PKCS7 S/MIME enclosure. "
++ "You may be able to view it by entering the correct passphrase "
++ "with the \"^D\" command. Press \"^E\" for more information.");
++
++ } else
++ #endif
+ if(type){
+ sstrcpy(&t, "\015\012");
+ switch(type) {
+diff -Ncr pine4.53/pine/makefile.lnx pine4.53-smime/pine/makefile.lnx
+*** pine4.53/pine/makefile.lnx Tue Sep 10 14:34:39 2002
+--- pine4.53-smime/pine/makefile.lnx Wed Jan 15 12:48:15 2003
+***************
+*** 60,79 ****
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
+ STDLIBS= -lncurses
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+--- 60,92 ----
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
++ SSLDIR= $(HOME)/local/ssl
++ SSLCERTS= $(SSLDIR)/certs
++ SSLINCLUDE= $(SSLDIR)/include
++ SSLLIB= $(SSLDIR)/lib
++
++ SSLCFLAGS= -I$(SSLINCLUDE) \
++ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
++ -DSMIME
++ SSLLDFLAGS= -L$(SSLLIB) -lcrypto
++
++
+ STDLIBS= -lncurses
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
++ $(SSLLDFLAGS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
++ $(SSLCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o bss_so.o smime.o smkeys.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+***************
+*** 135,137 ****
+--- 148,154 ----
+ osdep/sendmail osdep/execview \
+ osdep/postreap.wtp osdep/os-lnx.ic
+ cd osdep; $(MAKE) includer os-lnx.c; cd ..
++
++ jon.o: jon.c
++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -c $< -o $@
++
+diff -Ncr pine4.53/pine/makefile.so5 pine4.53-smime/pine/makefile.so5
+*** pine4.53/pine/makefile.so5 Tue Oct 23 15:24:51 2001
+--- pine4.53-smime/pine/makefile.so5 Wed Jan 15 12:48:15 2003
+***************
+*** 62,67 ****
+--- 62,78 ----
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
++ SSLDIR= $(HOME)/local/ssl
++ SSLCERTS= $(SSLDIR)/certs
++ SSLINCLUDE= $(SSLDIR)/include
++ SSLLIB= $(SSLDIR)/lib
++
++ SSLCFLAGS= -I$(SSLINCLUDE) \
++ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
++ -DSMIME
++ SSLLDFLAGS= -L$(SSLLIB) -lcrypto
++
++
+ # LDCC= /usr/bin/cc
+ # If you don't have /usr/bin/cc (our Solaris 2.2 doesn't seem to have it,
+ # it only has /usr/ucb/cc) then change LDCC to the following line and
+***************
+*** 72,88 ****
+ STDLIBS= -ltermlib
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+--- 83,101 ----
+ STDLIBS= -ltermlib
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
++ $(SSLLDFLAGS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
++ $(SSLCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o bss_so.o smime.o smkeys.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+diff -Ncr pine4.53/pine/pine.h pine4.53-smime/pine/pine.h
+*** pine4.53/pine/pine.h Fri Jan 10 15:25:55 2003
+--- pine4.53-smime/pine/pine.h Wed Jan 15 12:48:15 2003
+***************
+*** 1134,1139 ****
+--- 1134,1144 ----
+ F_DISABLE_SHARED_NAMESPACES,
+ F_EXPOSE_HIDDEN_CONFIG,
+ F_ALT_COMPOSE_MENU,
++ #ifdef SMIME
++ F_SIGN_DEFAULT_ON,
++ F_ENCRYPT_DEFAULT_ON,
++ F_REMEMBER_SMIME_PASSPHRASE,
++ #endif
+ F_ALWAYS_SPELL_CHECK,
+ F_QUELL_TIMEZONE,
+ F_COLOR_LINE_IMPORTANT,
+***************
+*** 2031,2036 ****
+--- 2036,2043 ----
+ } cmdline_val; /* user typed as cmdline arg */
+ };
+
++ #define MC_DECRYPT 800
++ #define MC_SECURITY 801
+
+
+ /*
+***************
+*** 4368,4373 ****
+--- 4375,4383 ----
+ char *pine_send_status PROTO((int, char *, char *, int *));
+ void phone_home PROTO((char *));
+ void pine_free_body PROTO((BODY **));
++ int pine_write_body_header PROTO((BODY *, soutr_t, TCPSTREAM *));
++ long pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
++ void pine_encode_body PROTO((BODY *));
+ void simple_header_parse PROTO((char *, char **, char **));
+ int valid_subject PROTO((char *, char **, char **,BUILDER_ARG *,int *));
+ long new_mail_for_pico PROTO((int, int));
+diff -Ncr pine4.53/pine/pine.hlp pine4.53-smime/pine/pine.hlp
+*** pine4.53/pine/pine.hlp Wed Jan 15 11:55:09 2003
+--- pine4.53-smime/pine/pine.hlp Wed Jan 15 12:48:15 2003
+***************
+*** 24338,24340 ****
+--- 24338,24347 ----
+ ========== h_select_by_smaller_size ==========
+ Enter a number or ^C to cancel. All messages less than this many characters
+ in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
++ ========== h_config_sign_default_on ==========
++ If enabled, the 'Sign' option will default to on when sending messages.
++ ========== h_config_encrypt_default_on ==========
++ If enabled, the 'Encrypt' option will default to on when sending messages.
++ ========== h_config_remember_smime_passphrase ==========
++ If enabled, you will only have to enter your passphrase for your private key
++ once during a pine session.
+diff -Ncr pine4.53/pine/send.c pine4.53-smime/pine/send.c
+*** pine4.53/pine/send.c Tue Jan 14 13:22:59 2003
+--- pine4.53-smime/pine/send.c Wed Jan 15 12:48:15 2003
+***************
+*** 50,55 ****
+--- 50,56 ----
+ #include "../c-client/smtp.h"
+ #include "../c-client/nntp.h"
+
++ #include "smime.h"
+
+ #ifndef TCPSTREAM
+ #define TCPSTREAM void
+***************
+*** 5490,5495 ****
+--- 5491,5513 ----
+ opts[i++].label = "";
+ }
+
++ #ifdef SMIME
++ {
++ opts[i].ch = 'e';
++ opts[i].rval = 'e';
++ opts[i].name = "E";
++ opts[i++].label = "Encrypt";
++
++ opts[i].ch = 'g';
++ opts[i].rval = 'g';
++ opts[i].name = "G";
++ opts[i++].label = "Sign";
++
++ g_do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON,ps_global);
++ g_do_sign = F_ON(F_SIGN_DEFAULT_ON,ps_global);
++ }
++ #endif
++
+ opts[i].ch = -1;
+ no_help = (i >= 12);
+
+***************
+*** 5574,5579 ****
+--- 5592,5627 ----
+ sstrcpy(&optp, dsn_string);
+ }
+
++ #ifdef SMIME
++ if (g_do_encrypt) {
++ if(!lparen){
++ *optp++ = ' ';
++ *optp++ = '(';
++ lparen++;
++ }
++ else{
++ *optp++ = ',';
++ *optp++ = ' ';
++ }
++
++ sstrcpy(&optp, "Encrypted");
++ }
++
++ if (g_do_sign) {
++ if(!lparen){
++ *optp++ = ' ';
++ *optp++ = '(';
++ lparen++;
++ }
++ else{
++ *optp++ = ',';
++ *optp++ = ' ';
++ }
++
++ sstrcpy(&optp, "Signed");
++ }
++ #endif
++
+ if(lparen)
+ *optp++ = ')';
+
+***************
+*** 5675,5680 ****
+--- 5723,5734 ----
+ * body on failure.
+ */
+ dsn_requested = (DSN_SHOW | DSN_SUCCESS | DSN_DELAY | DSN_FULL);
++ #ifdef SMIME
++ } else if (rv=='e') {
++ g_do_encrypt = !g_do_encrypt;
++ } else if (rv=='g') {
++ g_do_sign = !g_do_sign;
++ #endif
+ }
+
+ sprintf(dsn_string, "DSN requested[%s%s%s%s]",
+***************
+*** 6418,6423 ****
+--- 6472,6478 ----
+ char *verbose_file = NULL;
+ BODY *bp = NULL;
+ PINEFIELD *pf;
++ BODY *origBody = body;
+
+ #define MAX_ADDR_ERROR 2 /* Only display 2 address errors */
+
+***************
+*** 6434,6439 ****
+--- 6489,6518 ----
+ return(0);
+ }
+
++
++ #ifdef SMIME
++ if (g_do_encrypt || g_do_sign) {
++ int result;
++
++ STORE_S *so = lmc.so;
++ lmc.so = NULL;
++
++ result = 1;
++
++ if (g_do_encrypt)
++ result = encrypt_outgoing_message(header,&body);
++
++ /* need to free new body from encrypt if sign fails? */
++ if (result && g_do_sign)
++ result = sign_outgoing_message(header,&body,g_do_encrypt);
++
++ lmc.so = so;
++
++ if (!result)
++ return 0;
++ }
++ #endif
++
+ /* set up counts and such to keep track sent percentage */
+ send_bytes_sent = 0;
+ gf_filter_init(); /* zero piped byte count, 'n */
+***************
+*** 6742,6747 ****
+--- 6821,6844 ----
+ mail_free_envelope(&fake_env);
+
+ done:
++
++ #ifdef SMIME
++ /* Free replacement encrypted body */
++ if (body != origBody) {
++
++ if (body->type==TYPEMULTIPART) {
++ /* Just get rid of first part, it's actually origBody */
++ void *x = body->nested.part;
++
++ body->nested.part = body->nested.part->next;
++
++ fs_give(&x);
++ }
++
++ pine_free_body(&body);
++ }
++ #endif
++
+ if(we_cancel)
+ cancel_busy_alarm(0);
+
+***************
+*** 8721,8733 ****
+ dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+! if (!body->parameter) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
+ getpid ());
+! body->parameter = mail_newbody_parameter ();
+! body->parameter->attribute = cpystr ("BOUNDARY");
+! body->parameter->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do pine_encode_body (&part->body);
+--- 8818,8834 ----
+ dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+! if (!body->parameter || strucmp(body->parameter->attribute,"BOUNDARY")!=0) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
++ PARAMETER *param;
++
+ sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
+ getpid ());
+! param = mail_newbody_parameter ();
+! param->next = body->parameter;
+! param->attribute = cpystr ("BOUNDARY");
+! param->value = cpystr (tmp);
+! body->parameter = param;
+ }
+ part = body->nested.part; /* encode body parts */
+ do pine_encode_body (&part->body);
+diff -Ncr pine4.53/pine/smime.c pine4.53-smime/pine/smime.c
+*** pine4.53/pine/smime.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smime.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,1776 ----
++ /*
++ File: smime.c
++ Author: paisleyj@dcs.gla.ac.uk
++ Date: 01/2001
++
++ Description:
++ This file contains all the low-level functions
++ required for dealing with S/MIME objects.
++
++ References are made to the functions in this file
++ from the following locations:
++
++ mailview.c:part_desc() -> is_pkcs7_body()
++ send.c:call_mailer() -> encrypt_outgoing_message()
++ send.c:call_mailer() -> sign_outgoing_message()
++ mailcmd.c:process_cmd() -> get_passphrase()
++ mailcmd.c:process_cmd() -> smime_info_screen()
++ */
++
++ #ifdef SMIME
++
++ #include "headers.h"
++
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <time.h>
++
++ #include <openssl/err.h>
++ #include <openssl/objects.h>
++ #include <openssl/evp.h>
++
++ #include <openssl/x509.h>
++ #include <openssl/pkcs7.h>
++ #include <openssl/pem.h>
++ #include <openssl/rand.h>
++
++ #include "bss_so.h"
++ #include "smkeys.h"
++ #include "smime.h"
++
++ #define PINE_SMIME_DIRNAME ".pine-smime"
++
++ /* Set true if loading a key failed due to lack of passphrase.
++ Queried in mailcmd.c:process_cmd() before calling get_passphrase()
++ */
++ int g_need_passphrase = 0;
++ /* User has entered a passphrase */
++ static int s_entered_passphrase = 0;
++ /* Storage for the entered passphrase */
++ static char s_passphrase[80];
++ static char *s_passphrase_emailaddr;
++
++ /* Set true if encrypting/signing (respectively)
++ Referenced from send.c:call_mailer() and send.c:send_exit_for_pico
++ */
++ int g_do_encrypt;
++ int g_do_sign;
++
++ /* Full pathname to ~/.pine-smime */
++ static char *g_pine_smime_dir;
++
++ static BIO *bio_err;
++
++ /* Linked list of PERSONAL_CERT objects */
++ static PERSONAL_CERT *s_personal_certs;
++
++ static X509_STORE *s_cert_store;
++
++ /* State management for randomness functions below */
++ static int seeded = 0;
++ static int egdsocket = 0;
++
++ /* Forget any cached private keys */
++ static void forget_private_keys()
++ {
++ PERSONAL_CERT *pcert;
++
++ for (pcert=s_personal_certs; pcert; pcert=pcert->next) {
++
++ if (pcert->key) {
++ EVP_PKEY_free(pcert->key);
++ pcert->key = NULL;
++ }
++ }
++ }
++
++ /* taken from openssl/apps/app_rand.c */
++ static int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn)
++ {
++ int consider_randfile = (file == NULL);
++ char buffer[200];
++
++ if (file == NULL)
++ file = RAND_file_name(buffer, sizeof buffer);
++ else if (RAND_egd(file) > 0)
++ {
++ /* we try if the given filename is an EGD socket.
++ if it is, we don't write anything back to the file. */
++ egdsocket = 1;
++ return 1;
++ }
++ if (file == NULL || !RAND_load_file(file, -1))
++ {
++ if (RAND_status() == 0 && !dont_warn)
++ {
++ BIO_printf(bio_e,"unable to load 'random state'\n");
++ BIO_printf(bio_e,"This means that the random number generator has not been seeded\n");
++ BIO_printf(bio_e,"with much random data.\n");
++ if (consider_randfile) /* explanation does not apply when a file is explicitly named */
++ {
++ BIO_printf(bio_e,"Consider setting the RANDFILE environment variable to point at a file that\n");
++ BIO_printf(bio_e,"'random' data can be kept in (the file will be overwritten).\n");
++ }
++ }
++ return 0;
++ }
++ seeded = 1;
++ return 1;
++ }
++
++ /* copied and fiddled from pine/imap/src/osdep/unix/auth_ssl.c */
++ static void openssl_extra_randomness(void)
++ {
++ #if !defined(WIN32)
++ int fd;
++ unsigned long i;
++ char tmp[MAXPATH];
++ struct stat sbuf;
++ /* if system doesn't have /dev/urandom */
++ if (stat ("/dev/urandom",&sbuf)) {
++ if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0)
++ i = (unsigned long) tmp;
++ else {
++ unlink (tmp); /* don't need the file */
++ fstat (fd,&sbuf); /* get information about the file */
++ i = sbuf.st_ino; /* remember its inode */
++ close (fd); /* or its descriptor */
++ }
++ /* not great but it'll have to do */
++ sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx",
++ tcp_serverhost (),i,
++ (unsigned long) (time (0) ^ gethostid ()),
++ (unsigned long) getpid ());
++ RAND_seed (tmp,strlen (tmp));
++ }
++ #endif
++ }
++
++ /* taken from openssl/apps/app_rand.c */
++ static int app_RAND_write_file(const char *file, BIO *bio_e)
++ {
++ char buffer[200];
++
++ if (egdsocket || !seeded)
++ /* If we did not manage to read the seed file,
++ * we should not write a low-entropy seed file back --
++ * it would suppress a crucial warning the next time
++ * we want to use it. */
++ return 0;
++
++ if (file == NULL)
++ file = RAND_file_name(buffer, sizeof buffer);
++ if (file == NULL || !RAND_write_file(file))
++ {
++ BIO_printf(bio_e,"unable to write 'random state'\n");
++ return 0;
++ }
++ return 1;
++ }
++
++ /* Installed as an atexit() handler to save the random data */
++ static void openssl_deinit(void)
++ {
++ app_RAND_write_file(NULL, bio_err);
++ }
++
++ /* Initialise openssl stuff if needed */
++ static void openssl_init(void)
++ {
++ static int inited = 0;
++
++ if (!inited) {
++
++ char *p;
++ char buf[MAXPATH];
++
++ /* Find where that .pine-smime thing is */
++ /* Perhaps we should just use the user's home directory as a start? */
++ p = last_cmpnt(ps_global->pinerc);
++ buf[0] = '\0';
++ if(p != NULL) {
++ strncpy(buf, ps_global->pinerc, min(p - ps_global->pinerc, sizeof(buf)-1));
++ buf[min(p - ps_global->pinerc, sizeof(buf)-1)] = '\0';
++ }
++
++ strncat(buf, PINE_SMIME_DIRNAME, sizeof(buf)-1-strlen(buf));
++ buf[sizeof(buf)-1] = 0;
++
++ if (can_access(buf, ACCESS_EXISTS)==0) {
++
++ g_pine_smime_dir = cpystr(buf);
++ s_cert_store = get_ca_store(g_pine_smime_dir);
++ s_personal_certs = get_personal_certs(g_pine_smime_dir);
++
++ } else g_pine_smime_dir = ""; /* prevent null dereference later */
++
++ SSLeay_add_all_algorithms();
++ ERR_load_crypto_strings();
++
++ /* this won't make any sense (since the terminal's in a funny mode) */
++ if ((bio_err=BIO_new(BIO_s_file())) != NULL)
++ BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
++
++ app_RAND_load_file(NULL, bio_err, 1);
++
++ openssl_extra_randomness();
++
++ /* save the rand file when we're done */
++ atexit(openssl_deinit);
++
++ inited = 1;
++ }
++
++ ERR_clear_error();
++ }
++
++ /* Get a pointer to a string describing the most recent OpenSSL error.
++ It's statically allocated, so don't change or attempt to free it.
++ */
++ static const char *openssl_error_string(void)
++ {
++ char *errs;
++ const char *data = NULL;
++ long errn;
++
++ errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
++ errs = (char*)ERR_reason_error_string(ERR_GET_REASON(errn));
++
++ if (errs)
++ return errs;
++ else if (data)
++ return data;
++
++ return "unknown error";
++ }
++
++ /* Return true if the body looks like a PKCS7 object */
++ int is_pkcs7_body(BODY *body)
++ {
++ int result;
++
++ result = body->type==TYPEAPPLICATION &&
++ body->subtype &&
++ (strucmp(body->subtype,"pkcs7-mime")==0 ||
++ strucmp(body->subtype,"x-pkcs7-mime")==0 ||
++ strucmp(body->subtype,"pkcs7-signature")==0 ||
++ strucmp(body->subtype,"x-pkcs7-signature")==0);
++
++ return result;
++ }
++
++ /* debug utility to dump the contents of a BIO to a file */
++ static void dump_bio_to_file(BIO *in,char *filename)
++ {
++ char iobuf[4096];
++ int len;
++ BIO *out;
++
++ out = BIO_new_file(filename,"w");
++
++ if (out) {
++ BIO_reset(in);
++
++ while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
++ BIO_write(out, iobuf, len);
++ BIO_free(out);
++ }
++ }
++
++ /* prompt the user for their passphrase
++ (possibly prompting with the email address in s_passphrase_emailaddr)
++ */
++ int get_passphrase(void)
++ {
++ int rc;
++ int flags;
++ char prompt[50];
++ HelpType help = NO_HELP;
++
++ sprintf(prompt,
++ "Enter passphrase for <%s>: ",s_passphrase_emailaddr ? s_passphrase_emailaddr : "unknown");
++
++ do {
++ flags = OE_PASSWD | OE_DISALLOW_HELP;
++ rc = optionally_enter(s_passphrase, -FOOTER_ROWS(ps_global), 0, sizeof(s_passphrase),
++ prompt, NULL, help, &flags);
++ } while (rc!=0 && rc!=1 && rc>0);
++
++ if (rc==0)
++ s_entered_passphrase = 1;
++
++ return rc==0;
++ }
++
++ /* Recursively stash a pointer to the decrypted data in our
++ manufactured body.
++ */
++ static void create_local_cache(char *base,BODY *b)
++ {
++ if (b->type==TYPEMULTIPART) {
++ PART *p;
++
++ #if 0
++ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
++ #else
++ /* don't really want to copy the real body contents. It shouldn't be
++ used, and in the case of a message with attachments, we'll be
++ duplicating the files multiple times
++ */
++ cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
++ #endif
++
++ for (p=b->nested.part;p;p=p->next) {
++ create_local_cache(base,(BODY*) p);
++ }
++ } else {
++ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
++ }
++ }
++
++ static long rfc822_output_func(void *stream,char *string)
++ {
++ STORE_S *so = (STORE_S*) stream;
++
++ return so_puts(so,string)!=0;
++ }
++
++ /* Load a private key from the given file */
++ static EVP_PKEY *load_key(char *file, char *pass)
++ {
++ BIO *in;
++ EVP_PKEY *key;
++ if(!(in = BIO_new_file(file, "r"))) return NULL;
++ key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
++ BIO_free(in);
++ return key;
++ }
++
++ /* Attempt to load the private key for the given PERSONAL_CERT.
++ This sets the appropriate passphrase globals in order to
++ interact with the user correctly.
++ */
++ static int load_private_key(PERSONAL_CERT *pcert)
++ {
++ if (!pcert->key) {
++
++ /* Try empty password by default */
++ char *password = "";
++
++ if (g_need_passphrase) {
++ /* We've already been in here and discovered we need a different password */
++
++ if (s_entered_passphrase)
++ password = s_passphrase; /* Use the passphrase if it's been entered */
++ else return 0;
++ }
++
++ ERR_clear_error();
++
++ if(!(pcert->key = load_key(pcert->file, password))) {
++ long err = ERR_get_error();
++
++ /* Couldn't load key... */
++
++ if (s_entered_passphrase) {
++
++ /* The user got the password wrong maybe? */
++
++ if ((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
++ (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
++ q_status_message(SM_ORDER | SM_DING,1,1,"Wrong password");
++ else q_status_message1(SM_ORDER,1,1,"Couldn't read key: %s",(char*)openssl_error_string());
++
++ /* This passphrase is no good; forget it */
++ s_entered_passphrase = 0;
++ }
++
++ /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
++ g_need_passphrase = 1;
++
++ fs_give((void**) &s_passphrase_emailaddr);
++ s_passphrase_emailaddr = get_x509_subject_email(pcert->cert);
++ return 0;
++ } else {
++ /* This key will be cached, so we won't be called again */
++ s_entered_passphrase = 0;
++ g_need_passphrase = 0;
++ }
++
++ return 1;
++ }
++
++ return 0;
++ }
++
++ static void setup_pkcs7_body_for_signature(BODY *b,char *description,char *type,char *filename)
++ {
++ b->type = TYPEAPPLICATION;
++ b->subtype = cpystr(type);
++ b->encoding = ENCBINARY;
++
++ b->description = cpystr(description);
++
++ b->disposition.type = cpystr("attachment");
++ b->disposition.parameter = mail_newbody_parameter();
++ b->disposition.parameter->attribute = cpystr("filename");
++ b->disposition.parameter->value = cpystr(filename);
++
++ b->parameter = mail_newbody_parameter();
++ b->parameter->attribute = cpystr("name");
++ b->parameter->value = cpystr(filename);
++ }
++
++ /*
++ Look for a personal certificate matching the
++ given address
++ */
++ PERSONAL_CERT *match_personal_cert_to_email(ADDRESS *a)
++ {
++ PERSONAL_CERT *pcert;
++ char buf[MAXPATH];
++ char *email;
++
++ if (!a || !a->mailbox || !a->host)
++ return NULL;
++
++ snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
++
++ for (pcert=s_personal_certs;pcert;pcert=pcert->next) {
++
++ if (!pcert->cert)
++ continue;
++
++ email = get_x509_subject_email(pcert->cert);
++
++ if (email && strucmp(email,buf)==0) {
++ fs_give((void**) &email);
++ break;
++ }
++
++ fs_give((void**) &email);
++ }
++
++ return pcert;
++ }
++
++ /*
++ Look for a personal certificate matching the from
++ (or reply_to? in the given envelope)
++ */
++ PERSONAL_CERT *match_personal_cert(ENVELOPE *env)
++ {
++ PERSONAL_CERT *pcert;
++
++ pcert = match_personal_cert_to_email(env->reply_to);
++ if (!pcert)
++ pcert = match_personal_cert_to_email(env->from);
++
++ return pcert;
++ }
++
++ /*
++ Flatten the given body into its MIME representation.
++ Return the result in a CharStar STORE_S.
++ */
++ static STORE_S *body_to_store(BODY *body)
++ {
++ STORE_S *store;
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (!store)
++ return NULL;
++
++ pine_encode_body(body); /* this attaches random boundary strings to multiparts */
++ pine_write_body_header (body, rfc822_output_func,store);
++ pine_rfc822_output_body(body, rfc822_output_func,store);
++
++ /* now need to truncate by two characters since the above
++ appends CRLF to the stream
++ */
++
++ /** Eek! No way of telling size of a STORE_S. We depend on knowing it's
++ a CharStar */
++ so_truncate(store,((char*)store->eod-(char*)store->txt)-2);
++
++ so_seek(store,0,SEEK_SET);
++
++ return store;
++ }
++
++
++
++ /*
++ Sign a message. Called from call_mailer in send.c.
++
++ This takes the header for the outgoing message as well as a pointer
++ to the current body (which may be reallocated).
++ */
++ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach)
++ {
++ STORE_S *store = NULL;
++ STORE_S *outs = NULL;
++ BODY *body = *bodyP;
++ BODY *newBody = NULL;
++ PART *p1 = NULL;
++ PART *p2 = NULL;
++ PERSONAL_CERT *pcert;
++ BIO *in = NULL;
++ BIO *out = NULL;
++ PKCS7 *p7 = NULL;
++ int result = 0;
++ PARAMETER *param;
++
++ int flags = dont_detach ? 0 : PKCS7_DETACHED;
++
++ openssl_init();
++
++ store = body_to_store(body);
++
++ /* Look for a private key matching the sender address... */
++
++ pcert = match_personal_cert(header->env);
++
++ if (!pcert) {
++ q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to sign.");
++ goto end;
++ }
++
++ if (!load_private_key(pcert) && g_need_passphrase) {
++ /* Couldn't load key with blank password, try again */
++ get_passphrase();
++ load_private_key(pcert);
++ }
++
++ if (!pcert->key)
++ goto end;
++
++ in = BIO_new_so(store);
++
++ #if 0
++ dump_bio_to_file(in,"/tmp/signed-data");
++ #endif
++
++ BIO_reset(in);
++
++ p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
++ if (!p7) {
++ q_status_message(SM_ORDER,1,1,"Error creating PKCS7 object.");
++ goto end;
++ }
++
++ outs = so_get(CharStar,NULL,EDIT_ACCESS);
++ out = BIO_new_so(outs);
++
++ i2d_PKCS7_bio(out, p7);
++ BIO_flush(out);
++
++ so_seek(outs,0,SEEK_SET);
++
++ if ((flags&PKCS7_DETACHED)==0) {
++
++ /* the simple case: the signed data is in the pkcs7 object */
++
++ newBody = mail_newbody();
++
++ setup_pkcs7_body_for_signature(newBody,"S/MIME Cryptographically Signed Message","x-pkcs7-mime","smime.p7m");
++
++ newBody->contents.text.data = (char*) outs;
++ *bodyP = newBody;
++
++ result = 1;
++ } else {
++
++ /* OK.
++ We have to create a new body as follows:
++
++ multipart/signed; blah blah blah
++ reference to existing body
++
++ pkcs7 object
++ */
++
++ newBody = mail_newbody();
++
++ newBody->type = TYPEMULTIPART;
++ newBody->subtype = cpystr("signed");
++ newBody->encoding = ENC7BIT;
++
++ newBody->parameter = param = mail_newbody_parameter();
++ param->attribute = cpystr("protocol");
++ param->value = cpystr("application/x-pkcs7-signature");
++
++ newBody->parameter->next = param = mail_newbody_parameter();
++ param->attribute = cpystr("micalg");
++ param->value = cpystr("sha1");
++
++ p1 = mail_newbody_part();
++ p2 = mail_newbody_part();
++
++ /* this is nasty. We're just copying the body in here,
++ but since our newBody is freed at the end of call_mailer,
++ we mustn't let this body (the original one) be freed twice.
++ */
++ p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
++
++ p1->next = p2;
++
++ setup_pkcs7_body_for_signature(&p2->body,"S/MIME Cryptographic Signature","x-pkcs7-signature","smime.p7s");
++ p2->body.contents.text.data = (char*) outs;
++
++ newBody->nested.part = p1;
++
++ *bodyP = newBody;
++
++ result = 1;
++ }
++
++ end:
++
++ PKCS7_free(p7);
++ BIO_free(in);
++ BIO_free(out);
++ if (store)
++ so_give(&store);
++
++ return result;
++ }
++
++ /*
++ Encrypt a message on the way out. Called from call_mailer in send.c
++ The body may be reallocated.
++ */
++ int encrypt_outgoing_message(METAENV *header,BODY **bodyP)
++ {
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++ BIO *out = NULL;
++ EVP_CIPHER *cipher = NULL;
++ STACK_OF(X509) *encerts = NULL;
++ STORE_S *store = NULL;
++ STORE_S *outs = NULL;
++ PINEFIELD *pf;
++ ADDRESS *a;
++ BODY *body = *bodyP;
++ BODY *newBody = NULL;
++ int result = 0;
++
++ openssl_init();
++
++ cipher = EVP_des_cbc();
++
++ encerts = sk_X509_new_null();
++
++ /* Look for a certificate for each of the recipients */
++ for(pf = header->local; pf && pf->name; pf = pf->next)
++ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr) {
++
++ for (a=*pf->addr;a;a=a->next) {
++ X509 *cert;
++ char buf[MAXPATH];
++
++ snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
++
++ cert = get_cert_for(g_pine_smime_dir,buf);
++ if (cert)
++ sk_X509_push(encerts,cert);
++ else {
++ q_status_message2(SM_ORDER,1,1,
++ "Unable to find certificate for <%s@%s>",a->mailbox,a->host);
++ goto end;
++ }
++ }
++ }
++
++
++ store = body_to_store(body);
++
++ in = BIO_new_so(store);
++
++ p7 = PKCS7_encrypt(encerts, in, cipher, 0);
++
++ outs = so_get(CharStar,NULL,EDIT_ACCESS);
++ out = BIO_new_so(outs);
++
++ i2d_PKCS7_bio(out, p7);
++ BIO_flush(out);
++ so_seek(outs,0,SEEK_SET);
++
++ newBody = mail_newbody();
++
++ newBody->type = TYPEAPPLICATION;
++ newBody->subtype = cpystr("x-pkcs7-mime");
++ newBody->encoding = ENCBINARY;
++
++ newBody->description = cpystr("S/MIME Encrypted Message");
++
++ newBody->contents.text.data = (char*) outs;
++
++ *bodyP = newBody;
++
++ result = 1;
++
++ end:
++
++ BIO_free(in);
++ BIO_free(out);
++ PKCS7_free(p7);
++ sk_X509_pop_free(encerts, X509_free);
++ if (store)
++ so_give(&store);
++
++ return result;
++ }
++
++ /*
++ Plonk the contents (mime headers and body) of the given
++ section of a message to a CharStar STORE_S object.
++ */
++ static STORE_S *get_raw_part(int msgno,const char *section)
++ {
++ long len;
++ STORE_S *store = NULL;
++ char *text;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (store) {
++
++ /* First grab headers of the chap */
++ text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
++
++ if (text) {
++ so_nputs(store,text,len);
++
++ /** Now grab actual body */
++ text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
++ if (text) {
++ so_nputs(store,text,len);
++
++ so_seek(store,0,SEEK_SET);
++
++ } else so_give(&store);
++
++ } else so_give(&store);
++
++ }
++ return store;
++ }
++
++ /*
++ Get (and decode) the body of the given section of msg
++ */
++ static STORE_S *get_part_contents(int msgno,const char *section)
++ {
++ long len;
++ gf_io_t pc;
++ STORE_S *store = NULL;
++ char *err;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (store) {
++ gf_set_so_writec(&pc,store);
++
++ err = detach(ps_global->mail_stream, msgno, (char*) section,&len, pc, NULL);
++
++ gf_clear_so_writec(store);
++
++ so_seek(store,0,SEEK_SET);
++
++ if (err)
++ so_give(&store);
++ }
++ return store;
++ }
++
++ static PKCS7 *get_pkcs7_from_part(int msgno,const char *section)
++ {
++ STORE_S *store = NULL;
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++
++ store = get_part_contents(msgno,section);
++
++ if (store) {
++ in = BIO_new_so(store);
++ if (in) {
++ p7=d2i_PKCS7_bio(in,NULL);
++ }
++ }
++
++ if (store)
++ so_give(&store);
++
++ BIO_free(in);
++
++ return p7;
++ }
++
++ /*
++ Try to verify a signature.
++
++ p7 - the pkcs7 object to verify
++ in - the plain data to verify (NULL if not detached)
++ out - BIO to which to write the opaque data
++ */
++ static int do_signature_verify(PKCS7 *p7,BIO *in,BIO *out)
++ {
++ STACK_OF(X509) *otherCerts = NULL;
++ int result;
++ const char *data;
++ long err;
++
++ #if 0
++ dump_bio_to_file(in,"/tmp/verified-data");
++ #endif
++
++ BIO_reset(in);
++
++ #if 0
++ /* testing verification stuff */
++ {
++ X509 *c;
++
++ c = get_cert_for(g_pine_smime_dir,"xx");
++ if (c) {
++ X509_add1_reject_object(c, OBJ_nid2obj(NID_email_protect));
++
++ X509_STORE_add_cert(s_cert_store,c);
++
++ save_cert_for(g_pine_smime_dir,cpystr("yy"),c);
++ }
++
++
++ ERR_clear_error();
++
++ }
++ #endif
++
++ result = PKCS7_verify(p7, otherCerts, s_cert_store,
++ in, out, 0);
++
++ if (result) {
++ q_status_message(SM_ORDER,1,1,"S/MIME signature verified ok");
++ } else {
++ err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
++
++ if (out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)) {
++
++ /* Retry verification so we can get the plain text */
++ /* Might be better to reimplement PKCS7_verify here? */
++
++ PKCS7_verify(p7, otherCerts, s_cert_store,
++ in, out, PKCS7_NOVERIFY);
++
++ }
++
++ q_status_message1(SM_ORDER | SM_DING,1,1,"Couldn't verify S/MIME signature: %s",(char*) openssl_error_string());
++
++ return result;
++ }
++
++ /* now try to extract the certificates of any signers */
++ {
++ STACK_OF(X509) *signers;
++ int i;
++
++ signers = PKCS7_get0_signers(p7, NULL, 0);
++
++ if (signers)
++ for (i=0;i<sk_X509_num(signers);i++) {
++ char *email;
++ X509 *x = sk_X509_value(signers,i);
++ X509 *cert;
++
++ if (!x)
++ continue;
++
++ email = get_x509_subject_email(x);
++
++ if (email) {
++ cert = get_cert_for(g_pine_smime_dir,email);
++ if (cert) {
++ X509_free(cert);
++ } else {
++ save_cert_for(g_pine_smime_dir,email,x);
++ }
++ fs_give((void**) &email);
++ }
++ }
++
++ sk_X509_free(signers);
++ }
++
++ return result;
++ }
++
++ /* Hook inside BODY structure for cleaning up S/MIME message bodies */
++ static void smime_body_cleanup(BODY *b)
++ {
++ #if 0
++ q_status_message(SM_ORDER,1,1,"smime_body_cleanup called");
++ #endif
++
++ if (b->sparep) {
++ PKCS7_free((PKCS7*) b->sparep);
++ b->sparep = NULL;
++ }
++ }
++
++ /*
++ Given a multipart body of type multipart/signed, attempt to verify
++ it
++ */
++ static int do_detached_signature_verify(BODY *b,int msgno,char *section)
++ {
++ STORE_S *toVerify = NULL;
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++ PART *p;
++ int result = 0;
++ char seq[100];
++ char *what_we_did;
++
++ openssl_init();
++
++ snprintf(seq,sizeof(seq),"%s1",section);
++ toVerify = get_raw_part(msgno,seq);
++
++ if (toVerify) {
++
++ in = BIO_new_so(toVerify);
++ if (!in)
++ goto end;
++
++ snprintf(seq,sizeof(seq),"%s2",section);
++ p7 = get_pkcs7_from_part(msgno,seq);
++
++ if (!p7)
++ goto end;
++
++ result = do_signature_verify(p7,in,NULL);
++
++ if (b->subtype) fs_give((void**) &b->subtype);
++ b->subtype = cpystr("x-pkcs7-enclosure");
++ b->encoding = ENC8BIT;
++
++ if (b->description) fs_give ((void**) &b->description);
++
++ what_we_did = result ? "This message was cryptographically signed." :
++ "This message was cryptographically signed but the signature could not be verified.";
++
++ b->description = cpystr(what_we_did);
++
++ b->sparep = p7;
++ p7 = NULL;
++ b->cleanup = smime_body_cleanup;
++
++ p = b->nested.part;
++
++ /* p is signed plaintext */
++ if (p && p->next)
++ mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
++
++ result = 0;
++ }
++ end:
++ BIO_free(in);
++
++ PKCS7_free(p7);
++
++ if (toVerify)
++ so_give(&toVerify);
++
++ return result;
++ }
++
++ static PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
++ {
++ PERSONAL_CERT *x;
++
++ for (x=s_personal_certs;x;x=x->next) {
++ X509 *mine;
++
++ mine = x->cert;
++
++ if (!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
++ !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)) {
++ break;
++ }
++ }
++
++ return x;
++ }
++
++ static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7)
++ {
++ int i;
++ STACK_OF(PKCS7_RECIP_INFO) *recips;
++ PERSONAL_CERT *x = NULL;
++
++ recips = p7->d.enveloped->recipientinfo;
++
++ for (i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++) {
++ PKCS7_RECIP_INFO *ri;
++
++ ri=sk_PKCS7_RECIP_INFO_value(recips,i);
++
++ if ((x=find_certificate_matching_recip_info(ri))!=0) {
++ break;
++ }
++ }
++
++ return x;
++ }
++
++ /*
++ Try to decode (decrypt or verify a signature) a PKCS7 body
++ */
++ static int do_decoding(BODY *b,int msgno,const char *section)
++ {
++ STORE_S *outs = NULL;
++ int result = 0;
++
++ BIO *out = NULL;
++ PKCS7 *p7 = NULL;
++ X509 *recip = NULL;
++ EVP_PKEY *key = NULL;
++ PERSONAL_CERT *pcert = NULL;
++
++ char *what_we_did = "";
++
++ openssl_init();
++
++ /*
++ Extract binary data from part to an in-memory store
++ */
++
++ if (b->sparep) {
++
++ p7 = (PKCS7*) b->sparep;
++
++ } else {
++
++ p7 = get_pkcs7_from_part(msgno,section);
++ if (p7 == NULL) {
++ q_status_message1(SM_ORDER,1,1,"Couldn't load PKCS7 object: %s",(char*)openssl_error_string());
++ goto end;
++ }
++
++ /* Save the PKCS7 object for later dealings by the user interface.
++ It will be cleaned up when the body is garbage collected
++ */
++ b->sparep = p7;
++ b->cleanup = smime_body_cleanup;
++ }
++
++ if (PKCS7_type_is_signed(p7)) {
++ int sigok;
++
++ outs = so_get(CharStar, NULL, EDIT_ACCESS);
++ so_puts(outs,"MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
++ out = BIO_new_so(outs);
++
++ sigok = do_signature_verify(p7,NULL,out);
++
++ /* shouldn't really duplicate these messages */
++ what_we_did = sigok ? "This message was cryptographically signed." :
++ "This message was cryptographically signed but the signature could not be verified.";
++
++ } else if (!PKCS7_type_is_enveloped(p7)) {
++ q_status_message(SM_ORDER,1,1,"PKCS7 object not recognised.");
++ goto end;
++ } else { /* It *is* enveloped */
++
++ what_we_did = "This message was encrypted.";
++
++ /*
++ Now need to find a cert that can decrypt this boy
++ */
++ pcert = find_certificate_matching_pkcs7(p7);
++
++ if (!pcert) {
++ q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to decrypt.");
++ goto end;
++ }
++
++ recip = pcert->cert;
++
++ load_private_key(pcert);
++
++ key = pcert->key;
++ if (!key)
++ goto end;
++
++ outs = so_get(CharStar, NULL, EDIT_ACCESS);
++ so_puts(outs,"MIME-Version: 1.0\r\n");
++
++ out = BIO_new_so(outs);
++
++ if(!PKCS7_decrypt(p7, key, recip, out, 0)) {
++ q_status_message1(SM_ORDER,1,1,"Error decrypting PKCS7: %s",(char*) openssl_error_string());
++ goto end;
++ }
++
++ }
++
++ /* We've now produced a flattened MIME object in store outs.
++ It needs to be turned back into a BODY
++ */
++
++ {
++ BODY *body;
++ ENVELOPE *env;
++ char *h;
++ char *bstart;
++ STRING s;
++
++ h = so_text(outs);
++
++ /* look for start of body */
++ bstart = strstr(h,"\r\n\r\n");
++
++ if (!bstart) {
++ q_status_message(SM_ORDER,1,1,"Encrypted data couldn't be parsed.");
++ } else {
++ bstart += 4; /* skip over CRLF*2 */
++
++ INIT(&s,mail_string,bstart,strlen(bstart));
++ rfc822_parse_msg_full(&env,&body,h,bstart-h-2,&s,BADHOST,0,0);
++ mail_free_envelope(&env); /* Don't care about this */
++
++ /*
++ Now convert original body (application/pkcs7-mime)
++ to a multipart body with one sub-part (the decrypted body)
++ Note that the sub-part may also be multipart!
++ */
++
++ b->type = TYPEMULTIPART;
++ if (b->subtype) fs_give((void**) &b->subtype);
++
++ /* This subtype is used in mailview.c to annotate the display of
++ encrypted or signed messages. We know for sure then that it's a PKCS7
++ part because the sparep field is set to the PKCS7 object (see above)
++ */
++ b->subtype = cpystr("x-pkcs7-enclosure");
++ b->encoding = ENC8BIT;
++
++ if (b->description) fs_give ((void**) &b->description);
++ b->description = cpystr(what_we_did);
++
++ if (b->disposition.type) fs_give ((void **) &b->disposition.type);
++
++ if (b->contents.text.data) fs_give ((void **) &b->contents.text.data);
++
++ if (b->parameter) mail_free_body_parameter(&b->parameter);
++
++ /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
++ b->nested.part = fs_get(sizeof (PART));
++ b->nested.part->body = *body;
++ b->nested.part->next = NULL;
++
++ fs_give((void**) &body);
++
++ /* IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
++ data. Otherwise, it'll try to load it from the original data. Eek.
++ */
++ create_local_cache(bstart,&b->nested.part->body);
++
++ result = 1;
++ }
++ }
++
++ end:
++
++ BIO_free(out);
++
++ if (outs)
++ so_give(&outs);
++
++ return result;
++ }
++
++ /*
++ Recursively handle PKCS7 bodies in our message.
++
++ Returns non-zero if some fiddling was done.
++ */
++ static int do_fiddle_smime_message(BODY *b,int msgno,char *section)
++ {
++ int result = 0;
++
++ if (is_pkcs7_body(b)) {
++
++ if (do_decoding(b,msgno,*section ? section : "1")) {
++ /*
++ b should now be a multipart message: fiddle it too in case it's been multiply-encrypted!
++ */
++
++ /* fallthru */
++ result = 1;
++ }
++ }
++
++ if (b->type==TYPEMULTIPART) {
++
++ PART *p;
++ int partNum;
++ char newSec[100];
++
++ if (b->subtype && strucmp(b->subtype,"signed")==0) {
++
++ /* Ahah. We have a multipart signed entity. */
++
++ /* part 1 (signed thing)
++ part 2 (the pkcs7 object)
++ */
++
++ do_detached_signature_verify(b,msgno,section);
++
++ } else {
++
++ for (p=b->nested.part,partNum=1;p;p=p->next,partNum++) {
++
++ /* Append part number to the section string */
++
++ snprintf(newSec,sizeof(newSec),"%s%s%d",section,*section ? "." : "",partNum);
++
++ result |= do_fiddle_smime_message(&p->body,msgno,newSec);
++ }
++
++ }
++
++ }
++
++ return result;
++ }
++
++ /*
++ Fiddle a message in-place by decrypting/verifying S/MIME entities.
++ Returns non-zero if something was changed.
++ */
++
++ int fiddle_smime_message(BODY *b,int msgno,int is_new_message)
++ {
++ if (F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
++ forget_private_keys();
++ return do_fiddle_smime_message(b,msgno,"");
++ }
++
++
++ /********************************************************************************/
++
++ static struct key smime_info_keys[] =
++ {HELP_MENU,
++ OTHER_MENU,
++ {"<","Back",{MC_VIEW_TEXT,2,{'<',','}},KS_EXITMODE},
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ PREVPAGE_MENU,
++ NEXTPAGE_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++
++ HELP_MENU,
++ OTHER_MENU,
++ MAIN_MENU,
++ QUIT_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ INDEX_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ };
++ INST_KEY_MENU(smime_info_keymenu, smime_info_keys);
++
++ #define SMIME_PARENT_KEY 2
++
++ static void get_fingerprint(X509 *cert,const EVP_MD *type,char *buf,int maxLen)
++ {
++ unsigned char md[128];
++ char *b;
++ int len,i;
++
++ len = sizeof(md);
++
++ X509_digest(cert,type,md,&len);
++
++ b = buf;
++ *b = 0;
++ for (i=0; i<len; i++)
++ {
++ if (b-buf+3>=maxLen)
++ break;
++
++ if (i != 0)
++ *b++ = ':';
++ sprintf(b,"%02x",md[i]);
++ b+=2;
++ }
++ }
++
++ static void output_X509_NAME(X509_NAME *name,gf_io_t pc)
++ {
++ int i,c;
++ int nid;
++ char buf[256];
++
++ c = X509_NAME_entry_count(name);
++
++ for (i=c-1;i>=0;i--) {
++ X509_NAME_ENTRY *e;
++
++ e = X509_NAME_get_entry(name,i);
++ if (!e) continue;
++
++ X509_NAME_get_text_by_OBJ(name, e->object,buf,sizeof(buf));
++
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++ }
++
++ }
++
++ /*
++ Output a string in a distinctive style
++ */
++ static void gf_puts_uline(const char *txt,gf_io_t pc)
++ {
++ #if 0
++ pc(TAG_EMBED); pc(TAG_ULINEON);
++ gf_puts(txt,pc);
++ pc(TAG_EMBED); pc(TAG_ULINEOFF);
++ #else
++ pc(TAG_EMBED); pc(TAG_BOLDON);
++ gf_puts(txt,pc);
++ pc(TAG_EMBED); pc(TAG_BOLDOFF);
++ #endif
++ }
++
++ /*
++ Get a line from the given store (including \n)
++ */
++ static int so_gets(STORE_S *store,char *buf,int len)
++ {
++ unsigned char c;
++ char *bend = buf + len - 1;
++ char *b = buf;
++
++ do {
++ if (!store->readc(&c,store)) {
++ *b = 0;
++ return b!=buf;
++ }
++ *b++ = c;
++ } while (c!='\n' && b<bend);
++
++ *b = 0;
++
++ return 1;
++ }
++
++ /*
++ Wrap the text in the given store to the given width.
++ A new store is created for the result.
++ */
++ static STORE_S *wrap_store(STORE_S *in,int width)
++ {
++ STORE_S *result;
++ void *ws;
++ gf_io_t ipc,opc;
++
++ if (width<10)
++ width = 10;
++
++ result = so_get(CharStar,NULL,EDIT_ACCESS);
++ ws = gf_wrap_filter_opt(width,width,0,0);
++
++ gf_filter_init();
++ gf_link_filter(gf_wrap,ws);
++
++ gf_set_so_writec(&opc,result);
++ gf_set_so_readc(&ipc,in);
++
++ gf_pipe(ipc,opc);
++
++ gf_clear_so_readc(in);
++ gf_clear_so_writec(result);
++
++ return result;
++ }
++
++ /*
++ Output the contents of the given stores (left and right)
++ to the given gf_io_t.
++ The width of the terminal is inspected and two columns
++ are created to fit the stores into. They are then wrapped
++ and merged.
++ */
++ static void side_by_side(STORE_S *left,STORE_S *right,gf_io_t pc)
++ {
++ STORE_S *left_wrapped;
++ STORE_S *right_wrapped;
++
++ char buf_l[256];
++ char buf_r[256];
++ char *b;
++ int i;
++ int w = ps_global->ttyo->screen_cols/2 - 1;
++
++ so_seek(left,0,0);
++ so_seek(right,0,0);
++
++ left_wrapped = wrap_store(left,w);
++ right_wrapped = wrap_store(right,w);
++
++ so_seek(left_wrapped,0,0);
++ so_seek(right_wrapped,0,0);
++
++ for (;;) {
++
++ i = so_gets(left_wrapped,buf_l,sizeof(buf_l));
++ i += so_gets(right_wrapped,buf_r,sizeof(buf_r));
++
++ if (i==0)
++ break;
++
++ for (i=0, b=buf_l;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
++ pc(*b);
++ /* reduce accumulated width if an embed tag is discovered */
++ if (*b==TAG_EMBED)
++ i-=2;
++ }
++
++ if (buf_r[0]) {
++
++ while (i<w) {
++ pc(' ');
++ i++;
++ }
++
++ for (i=0, b=buf_r;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
++ pc(*b);
++ }
++ }
++
++ gf_puts(NEWLINE,pc);
++ }
++
++ so_give(&left_wrapped);
++ so_give(&right_wrapped);
++ }
++
++ static void print_separator_line(int percent,int ch,gf_io_t pc)
++ {
++ int i;
++ int start,len;
++
++ len = ps_global->ttyo->screen_cols * percent / 100;
++ start = (ps_global->ttyo->screen_cols - len)/2;
++
++ for (i=0;i<start;i++)
++ pc(' ');
++ for (i=start;i<start+len;i++)
++ pc(ch);
++ gf_puts(NEWLINE,pc);
++ }
++
++ static void output_cert_info(X509 *cert,gf_io_t pc)
++ {
++ char buf[256];
++ STORE_S *left,*right;
++ gf_io_t spc;
++ int i;
++
++ left = so_get(CharStar,NULL,EDIT_ACCESS);
++ right = so_get(CharStar,NULL,EDIT_ACCESS);
++
++ gf_set_so_writec(&spc,left);
++
++ if (!cert->cert_info) {
++ gf_puts("Couldn't find certificate info.",spc);
++ gf_puts(NEWLINE,spc);
++ } else {
++
++ gf_puts_uline("Subject (whose certificate it is)",spc);
++ gf_puts(NEWLINE, spc);
++
++ output_X509_NAME(cert->cert_info->subject,spc);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts_uline("Serial Number",spc);
++ gf_puts(NEWLINE,spc);
++
++ sprintf(buf,"%d",ASN1_INTEGER_get(cert->cert_info->serialNumber));
++ gf_puts(buf,spc);
++ gf_puts(NEWLINE,spc);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts_uline("Validity",spc);
++ gf_puts(NEWLINE,spc);
++ {
++ BIO *mb = BIO_new_so(left);
++
++ gf_puts("Not Before: ",spc);
++ ASN1_UTCTIME_print(mb,cert->cert_info->validity->notBefore);
++ BIO_flush(mb);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts("Not After: ",spc);
++ ASN1_UTCTIME_print(mb,cert->cert_info->validity->notAfter);
++ BIO_flush(mb);
++
++ gf_puts(NEWLINE,spc);
++ gf_puts(NEWLINE,spc);
++
++ BIO_free(mb);
++ }
++
++ }
++
++ gf_clear_so_writec(left);
++
++ gf_set_so_writec(&spc,right);
++
++ if (!cert->cert_info) {
++ gf_puts("Couldn't find certificate info.",spc);
++ gf_puts(NEWLINE,spc);
++ } else {
++ gf_puts_uline("Issuer",spc);
++ gf_puts(NEWLINE, spc);
++
++ output_X509_NAME(cert->cert_info->issuer,spc);
++ gf_puts(NEWLINE,spc);
++ }
++
++ gf_clear_so_writec(right);
++
++ side_by_side(left,right,pc);
++
++ gf_puts_uline("SHA1 Fingerprint",pc);
++ gf_puts(NEWLINE,pc);
++ get_fingerprint(cert,EVP_sha1(),buf,sizeof(buf));
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++
++ gf_puts_uline("MD5 Fingerprint",pc);
++ gf_puts(NEWLINE,pc);
++ get_fingerprint(cert,EVP_md5(),buf,sizeof(buf));
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++
++ so_give(&left);
++ so_give(&right);
++ }
++
++ void format_smime_info(int pass,BODY *body,int msgno,gf_io_t pc)
++ {
++ PKCS7 *p7;
++ int i;
++
++ if (body->type==TYPEMULTIPART) {
++ PART *p;
++
++ for (p=body->nested.part;p;p=p->next) {
++ format_smime_info(pass,&p->body,msgno,pc);
++ }
++ }
++
++ p7 = body->sparep;
++ if (p7) {
++
++ if (PKCS7_type_is_signed(p7)) {
++ STACK_OF(X509) *signers;
++
++ switch (pass) {
++ case 1:
++ gf_puts("This message was cryptographically signed." NEWLINE,pc);
++ break;
++ case 2:
++
++ signers = PKCS7_get0_signers(p7, NULL, 0);
++
++ if (signers) {
++
++ sprintf(tmp_20k_buf,"Certificate%s used for signing",plural(sk_X509_num(signers)));
++ gf_puts_uline(tmp_20k_buf,pc);
++ gf_puts(NEWLINE,pc);
++ print_separator_line(100,'-',pc);
++
++ for (i=0;i<sk_X509_num(signers);i++) {
++ X509 *x = sk_X509_value(signers,i);
++
++ if (x) {
++ output_cert_info(x,pc);
++ gf_puts(NEWLINE,pc);
++ }
++
++ }
++ }
++
++ sk_X509_free(signers);
++ break;
++ }
++
++ } else if (PKCS7_type_is_enveloped(p7)) {
++
++ switch (pass) {
++ case 1:
++ gf_puts("This message was encrypted." NEWLINE,pc);
++ break;
++ case 2:
++
++ if (p7->d.enveloped && p7->d.enveloped->enc_data) {
++ X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
++ STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
++ int found = 0;
++
++ gf_puts("The algorithm used to encrypt was ",pc);
++
++
++ if (alg) {
++ char *n = OBJ_nid2sn( OBJ_obj2nid(alg->algorithm ));
++
++ gf_puts(n ? n : "<unknown>",pc);
++
++ } else gf_puts("<unknown>",pc);
++
++ gf_puts("." NEWLINE NEWLINE,pc);
++
++ sprintf(tmp_20k_buf,"Certificate%s for decrypting",plural(sk_PKCS7_RECIP_INFO_num(ris)));
++ gf_puts_uline(tmp_20k_buf,pc);
++ gf_puts(NEWLINE,pc);
++ print_separator_line(100,'-',pc);
++
++ for (i=0;i<sk_PKCS7_RECIP_INFO_num(ris);i++) {
++ PKCS7_RECIP_INFO *ri;
++ PERSONAL_CERT *pcert;
++
++ ri = sk_PKCS7_RECIP_INFO_value(ris,i);
++ if (!ri) continue;
++
++ pcert = find_certificate_matching_recip_info(ri);
++
++ if (pcert) {
++
++ if (found) {
++ print_separator_line(25,'*',pc);
++ gf_puts(NEWLINE,pc);
++ }
++ found = 1;
++
++ output_cert_info(pcert->cert,pc);
++ gf_puts(NEWLINE,pc);
++
++ }
++ }
++
++ if (!found) {
++ gf_puts("No certificate capable of decrypting could not be found.",pc);
++ }
++ }
++ break;
++ }
++
++ }
++ }
++ }
++
++ void view_writec();
++
++ void smime_info_screen(struct pine *ps)
++ {
++ int msgno;
++ OtherMenu what;
++ int cmd;
++ char backtag[64];
++ BODY *body;
++ ENVELOPE *env;
++ HANDLE_S *handles = NULL;
++ SCROLL_S scrollargs;
++ STORE_S *store = NULL;
++ int offset = 0;
++
++ ps->prev_screen = smime_info_screen;
++ ps->next_screen = SCREEN_FUN_NULL;
++
++ if(mn_total_cur(ps->msgmap) > 1L){
++ q_status_message(SM_ORDER | SM_DING, 0, 3,
++ "Can only view one message's information at a time.");
++ return;
++ }
++ /* else check for existence of smime bits */
++
++ msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
++
++ env = mail_fetch_structure (ps->mail_stream,msgno,
++ &body,0);
++ if (!env || !body) {
++ q_status_message(SM_ORDER, 0, 3,
++ "Can't fetch body of message.");
++ return;
++ }
++
++ what = FirstMenu;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++
++ while(ps->next_screen == SCREEN_FUN_NULL){
++
++ ClearLine(1);
++
++ so_truncate(store,0);
++
++ view_writec_init(store, &handles, HEADER_ROWS(ps),
++ HEADER_ROWS(ps) +
++ ps->ttyo->screen_rows - (HEADER_ROWS(ps)
++ + HEADER_ROWS(ps)));
++
++ gf_puts_uline("Overview",view_writec);
++ gf_puts(NEWLINE,view_writec);
++
++ format_smime_info(1,body,msgno,view_writec);
++ gf_puts(NEWLINE,view_writec);
++ format_smime_info(2,body,msgno,view_writec);
++
++ view_writec_destroy();
++
++
++ ps->next_screen = SCREEN_FUN_NULL;
++
++ memset(&scrollargs, 0, sizeof(SCROLL_S));
++ scrollargs.text.text = so_text(store);
++ scrollargs.text.src = CharStar;
++ scrollargs.text.desc = "S/MIME information";
++ scrollargs.body_valid = 1;
++
++ if(offset){ /* resize? preserve paging! */
++ scrollargs.start.on = Offset;
++ scrollargs.start.loc.offset = offset;
++ offset = 0L;
++ }
++
++ scrollargs.bar.title = "S/MIME INFORMATION";
++ /* scrollargs.end_scroll = view_end_scroll; */
++ scrollargs.resize_exit = 1;
++ scrollargs.help.text = NULL;
++ scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
++ scrollargs.keys.menu = &smime_info_keymenu;
++ scrollargs.keys.what = what;
++ setbitmap(scrollargs.keys.bitmap);
++
++ if(scrolltool(&scrollargs) == MC_RESIZE)
++ offset = scrollargs.start.loc.offset;
++ }
++
++ so_give(&store);
++
++ }
++
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smime.h pine4.53-smime/pine/smime.h
+*** pine4.53/pine/smime.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smime.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,17 ----
++ #ifdef SMIME
++
++ int is_pkcs7_body(BODY *b);
++
++ int fiddle_smime_message(BODY *b,int msgno,int is_new_message);
++
++ int encrypt_outgoing_message(METAENV *header,BODY **bodyP);
++ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach);
++ int get_passphrase(void);
++ void smime_info_screen(struct pine *ps);
++
++ extern int g_need_passphrase;
++
++ extern int g_do_encrypt;
++ extern int g_do_sign;
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smkeys.c pine4.53-smime/pine/smkeys.c
+*** pine4.53/pine/smkeys.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smkeys.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,343 ----
++ #ifdef SMIME
++
++ #include "headers.h"
++
++ #include <openssl/err.h>
++ #include <openssl/objects.h>
++ #include <openssl/evp.h>
++ #include <openssl/x509.h>
++ #include <openssl/pkcs7.h>
++ #include <openssl/pem.h>
++
++ #include "smkeys.h"
++
++ /*---------------------------------------------------
++ Remove leading whitespace, trailing whitespace and convert
++ to lowercase. Also remove slash characters
++
++ Args: s, -- The string to clean
++
++ Result: the cleaned string
++ ----*/
++ static char *
++ emailstrclean(string)
++ char *string;
++ {
++ char *s = string, *sc = NULL, *p = NULL;
++
++ for(; *s; s++){ /* single pass */
++ if(!isspace((unsigned char)*s)){
++ p = NULL; /* not start of blanks */
++ if(!sc) /* first non-blank? */
++ sc = string; /* start copying */
++ }
++ else if(!p) /* it's OK if sc == NULL */
++ p = sc; /* start of blanks? */
++
++ if(sc && *s!='/' && *s!='\\') /* if copying, copy */
++ *sc++ = isupper((unsigned char)(*s))
++ ? (unsigned char)tolower((unsigned char)(*s))
++ : (unsigned char)(*s);
++ }
++
++ if(p) /* if ending blanks */
++ *p = '\0'; /* tie off beginning */
++ else if(!sc) /* never saw a non-blank */
++ *string = '\0'; /* so tie whole thing off */
++
++ return(string);
++ }
++
++ /*
++ Add a lookup for each "*.crt" file in the given directory.
++ */
++ static void add_certs_in_dir(X509_LOOKUP *lookup,const char *path)
++ {
++ char buf[MAXPATH];
++ struct direct *d;
++ DIR *dirp;
++ PERSONAL_CERT *result;
++
++ result = NULL;
++
++ dirp = opendir(path);
++ if (dirp) {
++
++ while (d=readdir(dirp)) {
++ BIO *in;
++ X509 *cert;
++
++ if (srchrstr(d->d_name,".crt")) {
++
++ build_path(buf,(char*) path,d->d_name,sizeof(buf));
++
++ X509_LOOKUP_load_file(lookup,buf,X509_FILETYPE_PEM);
++ }
++
++ }
++
++ closedir(dirp);
++ }
++ }
++
++ /* Get an X509_STORE. This consists of the system
++ certs directory and any certificates in the user's
++ ~/.pine-smime/ca directory.
++ */
++ X509_STORE *get_ca_store(const char *path)
++ {
++ X509_LOOKUP *lookup;
++ char buf[MAXPATH];
++
++ X509_STORE *store;
++
++ store=X509_STORE_new();
++
++ lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
++
++ build_path(buf,(char*) path,"ca",sizeof(buf));
++
++ add_certs_in_dir(lookup,buf);
++
++ /* X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); */
++
++ lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
++
++ X509_LOOKUP_add_dir(lookup,SSL_CERT_DIRECTORY,X509_FILETYPE_PEM);
++
++ /* X509_STORE_set_default_paths(cert_store);
++ X509_STORE_load_locations(cert_store,NULL,"../../certs");
++ X509_STORE_set_verify_cb_func(cert_store,verify_callback);
++ */
++
++ return store;
++ }
++
++ static EVP_PKEY *load_key(char *file, char *pass)
++ {
++ BIO *in;
++ EVP_PKEY *key;
++ if(!(in = BIO_new_file(file, "r"))) return NULL;
++ key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
++ BIO_free(in);
++ return key;
++ }
++
++ char *get_x509_name_entry(const char *key,X509_NAME *name)
++ {
++ int i,c,n;
++ char buf[256];
++ char *id;
++
++ if (!name)
++ return NULL;
++
++ c = X509_NAME_entry_count(name);
++
++ for (i=0;i<c;i++) {
++ X509_NAME_ENTRY *e;
++
++ e = X509_NAME_get_entry(name,i);
++ if (!e) continue;
++
++ buf[0] = 0;
++ id = buf;
++
++ n = OBJ_obj2nid(e->object);
++ if ((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)) {
++ i2t_ASN1_OBJECT(buf,sizeof(buf),e->object);
++ id = buf;
++ }
++
++ if ((strucmp(id,"email")==0) || (strucmp(id,"emailAddress")==0)) {
++ X509_NAME_get_text_by_OBJ(name, e->object,(char*) buf,sizeof(buf)-1);
++ return cpystr(buf);
++ }
++ }
++ return NULL;
++ }
++
++ char *get_x509_subject_email(X509 *x)
++ {
++ char* result;
++ result = get_x509_name_entry("email",X509_get_subject_name(x));
++ if ( !result ) {
++ result = get_x509_name_entry("emailAddress",X509_get_subject_name(x));
++ }
++
++ return result;
++ }
++
++ /* Save the certificate for the given email address in
++ ~/.pine-smime/public.
++
++ Should consider the security hazards in making a file with
++ the email address that has come from the certificate.
++
++ The argument email is destroyed.
++ */
++
++ void save_cert_for(const char *path,char *email,X509 *cert)
++ {
++ char sf[MAXPATH];
++ char sf2[MAXPATH];
++ BIO *tmp;
++
++ build_path(sf,(char*) path,"public",sizeof(sf));
++
++ build_path(sf2,sf,emailstrclean(email),sizeof(sf2));
++ strncat(sf2,".crt",sizeof(sf2)-1-strlen(sf2));
++ sf2[sizeof(sf2)-1] = 0;
++
++ tmp = BIO_new_file(sf2, "w");
++ if (tmp) {
++ X509_print(tmp,cert);
++ PEM_write_bio_X509_AUX(tmp, cert);
++ BIO_free(tmp);
++ q_status_message1(SM_ORDER,1,1,"Saved certificate for <%s>",(char*)email);
++ } else {
++ q_status_message1(SM_ORDER,1,1,"Couldn't save certificate for <%s>",(char*)email);
++ }
++ }
++
++ /*
++ Try to retrieve the certificate for the given email address.
++ */
++ X509 *get_cert_for(const char *path,const char *email)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ char buf3[MAXPATH];
++ char *p;
++ X509 *cert = NULL;
++ BIO *in;
++
++ strncpy(buf3,email,sizeof(buf3)-1);
++ buf3[sizeof(buf3)-1] = 0;
++
++ /* clean it up (lowercase, space removal) */
++ emailstrclean(buf3);
++
++ build_path(buf,(char*)path,"public",sizeof(buf));
++ build_path(buf2,buf,buf3,sizeof(buf2));
++ strncat(buf2,".crt",sizeof(buf2)-1-strlen(buf2));
++ buf2[sizeof(buf2)-1] = 0;
++
++ if((in = BIO_new_file(buf2, "r"))!=0) {
++
++ cert = PEM_read_bio_X509(in, NULL, NULL,NULL);
++
++ if (cert) {
++ /* could check email addr in cert matches */
++ }
++ }
++ BIO_free(in);
++
++ return cert;
++ }
++
++ EVP_PKEY *get_key_for(const char *path,const char *email,const char *pass)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ char buf3[MAXPATH];
++ char *p;
++ EVP_PKEY *key = NULL;
++ BIO *in;
++
++ strncpy(buf3,email,sizeof(buf3)-1);
++ buf3[sizeof(buf3)-1] = 0;
++
++ /* clean it up (lowercase, space removal) */
++ emailstrclean(buf3);
++
++ build_path(buf,(char*)path,"private",sizeof(buf));
++ build_path(buf2,buf,buf3,sizeof(buf2));
++ strncat(buf2,".key",sizeof(buf2)-1-strlen(buf2));
++ buf2[sizeof(buf2)-1] = 0;
++
++ key = load_key(buf2,pass);
++
++ return key;
++ }
++
++ /*
++ Load the user's personal certificates from
++ ~/.pine-smime/private
++ */
++ PERSONAL_CERT *get_personal_certs(const char *path)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ struct direct *d;
++ DIR *dirp;
++ PERSONAL_CERT *result;
++
++ result = NULL;
++
++ build_path(buf,(char*) path,"private",sizeof(buf));
++
++ dirp = opendir(buf);
++ if (dirp) {
++
++ while (d=readdir(dirp)) {
++ BIO *in;
++ X509 *cert;
++
++ if (srchrstr(d->d_name,".key")) {
++
++ /* copy file name to temp buffer */
++ strcpy(buf2,d->d_name);
++ /* chop off ".key" trailier */
++ buf2[strlen(buf2)-4] = 0;
++ /* Look for certificate */
++ cert = get_cert_for(path,buf2);
++
++ if (cert) {
++ PERSONAL_CERT *pc;
++
++ /* create a new PERSONAL_CERT, fill it in */
++
++ pc = fs_get(sizeof(PERSONAL_CERT));
++ pc->cert = cert;
++ build_path(buf2,buf,d->d_name,sizeof(buf2));
++ pc->file = cpystr(buf2);
++
++ strcpy(pc->file + strlen(pc->file) - 4, ".key");
++
++ /* Try to load the key with an empty password */
++ pc->key = load_key(pc->file,"");
++
++ pc->next = result;
++ result = pc;
++ }
++ }
++
++ }
++
++ closedir(dirp);
++ }
++
++ return result;
++ }
++
++ void personal_cert_free(PERSONAL_CERT **pcp)
++ {
++ if (pcp && *pcp) {
++
++ PERSONAL_CERT *pc = *pcp;
++
++ fs_give((void**) &pc->file);
++
++ X509_free(pc->cert);
++
++ if (pc->key)
++ EVP_PKEY_free(pc->key);
++
++ personal_cert_free(&pc->next);
++
++ fs_give((void**) pcp);
++ }
++ }
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smkeys.h pine4.53-smime/pine/smkeys.h
+*** pine4.53/pine/smkeys.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smkeys.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,20 ----
++
++ #define PERSONAL_CERT struct personal_cert
++
++ PERSONAL_CERT {
++ X509 *cert;
++ EVP_PKEY *key;
++ char *file;
++ PERSONAL_CERT *next;
++ };
++
++ X509_STORE *get_ca_store(const char *d);
++
++ PERSONAL_CERT *get_personal_certs(const char *d);
++
++ X509 *get_cert_for(const char *path,const char *email);
++ void save_cert_for(const char *path,char *email,X509 *cert);
++
++ void personal_cert_free(PERSONAL_CERT **pc);
++
++ char *get_x509_subject_email(X509 *x);