diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /contrib/smime/pine-smime-20030115.diff | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'contrib/smime/pine-smime-20030115.diff')
-rw-r--r-- | contrib/smime/pine-smime-20030115.diff | 3265 |
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); |