diff -ruN original.pine4.61.sources/pine4.61/imap/src/c-client/mail.c smime.pine.for.Linux/pine4.61/imap/src/c-client/mail.c --- original.pine4.61.sources/pine4.61/imap/src/c-client/mail.c Fri Jul 9 00:02:03 2004 +++ smime.pine.for.Linux/pine4.61/imap/src/c-client/mail.c Fri Jul 16 23:17:46 2004 @@ -5494,6 +5494,10 @@ 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 -ruN original.pine4.61.sources/pine4.61/imap/src/c-client/mail.h smime.pine.for.Linux/pine4.61/imap/src/c-client/mail.h --- original.pine4.61.sources/pine4.61/imap/src/c-client/mail.h Tue Jun 22 03:06:58 2004 +++ smime.pine.for.Linux/pine4.61/imap/src/c-client/mail.h Fri Jul 16 23:17:46 2004 @@ -708,6 +708,7 @@ } size; char *md5; /* MD5 checksum */ void *sparep; /* spare pointer reserved for main program */ + void (*cleanup)(BODY *); /* cleanup function */ }; diff -ruN original.pine4.61.sources/pine4.61/pine/README.smime smime.pine.for.Linux/pine4.61/pine/README.smime --- original.pine4.61.sources/pine4.61/pine/README.smime Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/README.smime Fri Jul 16 23:17:46 2004 @@ -0,0 +1,177 @@ +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/ + ++ If a directory '~/.pine-smime` is not present, it'll be created by client. Within ++ this, a futher three directories are created too: ++ ~/.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. ++ CAs certificates and CRLs should be put in the 'ca' and 'SSL_CERT_DIRECTORY' directories. ++ All documents should be here in standard hashed format. + +# There are three extra configuration options: +# +# sign-default-on +# encrypt-default-on +# remember-smime-passphrase + ++ There are extra configuration options: ++ ++ CA-dir ++ Private-dir ++ Public-dir ++ Certfile ++ TSA ++ smime-options: ++ verify-on, ++ crl-check, ++ crl-check-all, ++ crls-from-CAs, ++ ocsp, ++ use-SSL_CERT_DIRECTORY, ++ sign-default-on, ++ encrypt-default-on, ++ time-stamp-default-on, ++ remember-smime-passphrase, ++ save-certs-CRLs. + +Certificates +============ + +The certificate files specified above should have the following form: + + public certificates: user@emaildomain.crt + private keys: user@emaildomain.key + ++ 'user@emaildomain' is expected to be included in certificate subject field + +Thus, a typical installation might look like this: + +# ~/.pine-smime/ ++ ~/.pine-smime/ + ca/ +# [additional trusted CAs here] ++ trusted CAs certs and CRLs. + 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 ++ certificate status verification - CRL, OCSP. + smkeys.c Very basic X509 key handling/storage (using the above dirs) + bss_so.c OpenSSL BIO using pine STORE_S objects + ++ header files : smkeys.h, smime.h, smime.error.h, bss_co.h + + +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. ++ Added function to convert changed S/MIME bodies back to original. + + 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. ++ Added 'Encrypt', 'Sign', 'Time stamp' 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. + ++ other.c ++ Added S/MIME configuration to (S)etup->(C)config menu. + + +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 ++ PKCS7 parts, decode them and attempt to decrypt/verify signatures/verify time stampes. 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 ++ and pine_rfc822_output_body, encrypting/signing/time stamping 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 + ++ kouril.martin@seznam.cz ++ Jan 9 2004 diff -ruN original.pine4.61.sources/pine4.61/pine/TODO.smime smime.pine.for.Linux/pine4.61/pine/TODO.smime --- original.pine4.61.sources/pine4.61/pine/TODO.smime Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/TODO.smime Fri Jul 16 23:17:46 2004 @@ -0,0 +1,67 @@ + + 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. ++ - but I think there's terrible english, maybe some native should ++ re-create it. My english isn't good. ++ ++ when no data is included in S/MIME message, Pine screams "Malformed message". ++ ++ use CRLs included in incoming PKCS7 structure. ++ ++ use time stampes if certificate has experid (when decrypting,..). ++ ++ for OpenSSL0.9.7d is encrypting unstable ++ it looks like not my fault (once in my life) :), like some problem ++ inside OpenSSL. ++ ++ # screen for outoging errors ++ ++ make more detailed errors. ++ ++ to get timestamp from TSA takes too long. ++ -it seems TSA is waiting for something. OpenTSA client ++ use perl + libcurl and it's faster.I dodn't know ++ what it's wrong. ++ ++ test it. ++ ++ S/MIME options isn't stable on Slackware Linux. ++ ++ source need more comments ++ ++ make some documentation ++ ++ more and more :) + + + +paisleyj@dcs.gla.ac.uk +Mar 7 2001 + ++ kouril.martin@seznam.cz ++ Jan 9 2004 diff -ruN original.pine4.61.sources/pine4.61/pine/bss_so.c smime.pine.for.Linux/pine4.61/pine/bss_so.c --- original.pine4.61.sources/pine4.61/pine/bss_so.c Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/bss_so.c Fri Jul 16 23:17:46 2004 @@ -0,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 +#include +#include +#include + +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 -ruN original.pine4.61.sources/pine4.61/pine/bss_so.h smime.pine.for.Linux/pine4.61/pine/bss_so.h --- original.pine4.61.sources/pine4.61/pine/bss_so.h Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/bss_so.h Fri Jul 16 23:17:46 2004 @@ -0,0 +1 @@ +BIO *BIO_new_so(STORE_S *so); diff -ruN original.pine4.61.sources/pine4.61/pine/filter.c smime.pine.for.Linux/pine4.61/pine/filter.c --- original.pine4.61.sources/pine4.61/pine/filter.c Fri Jul 9 01:39:42 2004 +++ smime.pine.for.Linux/pine4.61/pine/filter.c Fri Jul 16 23:17:46 2004 @@ -965,7 +965,7 @@ /* get a character from a file */ -/* assumes gf_out struct is filled in */ +/* assumes gf_in struct is filled in */ int gf_freadc(c) unsigned char *c; @@ -1013,7 +1013,7 @@ } /* get a character from a string, return nonzero if things OK */ -/* assumes gf_out struct is filled in */ +/* assumes gf_in struct is filled in */ int gf_sreadc(c) unsigned char *c; diff -ruN original.pine4.61.sources/pine4.61/pine/init.c smime.pine.for.Linux/pine4.61/pine/init.c --- original.pine4.61.sources/pine4.61/pine/init.c Thu Jul 15 19:37:24 2004 +++ smime.pine.for.Linux/pine4.61/pine/init.c Fri Jul 16 23:17:46 2004 @@ -67,7 +67,6 @@ #include "headers.h" #include "../c-client/imap4r1.h" /* for LEVELSTATUS() */ - typedef enum {Sapling, Seedling, Seasoned} FeatureLevel; #define TO_BAIL_THRESHOLD 60 @@ -415,6 +414,19 @@ CONF_TXT_T init_md_create[] = "Creating subdirectory \"%s\" where Pine will store its mail folders."; +#ifdef SMIME +CONF_TXT_T cf_text_CApath[] = "...smime CApath..."; + +CONF_TXT_T cf_text_key_cert_path[] = "...smime private key and certificate to sign/encrypt/decrypt..."; + +CONF_TXT_T cf_text_public_path[] = "...public path..."; + +CONF_TXT_T cf_text_smime_options[] = "...smime options..."; + +CONF_TXT_T cf_text_certfile[] = "....certfile..."; + +CONF_TXT_T cf_text_TSA[] = "...tsa..."; +#endif /*---------------------------------------------------------------------- These are the variables that control a number of pine functions. They @@ -611,6 +623,23 @@ cf_text_permlocked}, {"dead-letter-files", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, cf_text_deadlets}, + +#ifdef SMIME +/* S/MIME options */ +{"CA-dir", 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + cf_text_CApath}, +{"private-dir", 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + cf_text_key_cert_path}, +{"public-dir", 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + cf_text_public_path}, +{"Certfile", 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + cf_text_certfile}, +{"TSA", 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + cf_text_TSA}, +{"smime-options", 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, + cf_text_smime_options}, +#endif + /* * Starting here, the variables are hidden in the Setup/Config screen. * They are exposed if feature expose-hidden-config is set. @@ -1548,7 +1577,6 @@ */ fixedprc = new_pinerc_s(SYSTEM_PINERC_FIXED); #endif - /* * cache-remote-pinerc is experimental and unannounced as of 4.40. * (Set it with cmdline -feature-list=cache-remote-pinerc.) @@ -1842,6 +1870,11 @@ set_current_val(&vars[V_KEYWORDS], TRUE, TRUE); ps_global->keywords = init_keyword_list(VAR_KEYWORDS); + +#ifdef SMIME + /* set S/MIME options -> smime.c */ + set_smime_vars(); +#endif set_current_val(&vars[V_OPER_DIR], TRUE, TRUE); if(VAR_OPER_DIR && !VAR_OPER_DIR[0]){ diff -ruN original.pine4.61.sources/pine4.61/pine/mailcmd.c smime.pine.for.Linux/pine4.61/pine/mailcmd.c --- original.pine4.61.sources/pine4.61/pine/mailcmd.c Wed Jul 14 03:35:15 2004 +++ smime.pine.for.Linux/pine4.61/pine/mailcmd.c Fri Jul 16 23:17:46 2004 @@ -53,6 +53,9 @@ #include "headers.h" #include "../c-client/imap4r1.h" +#ifdef SMIME +#include "smime.h" +#endif /* * Internal Prototypes @@ -1481,6 +1484,19 @@ break; +#ifdef SMIME + /*------- Try to decrypt message -----------*/ + case MC_DECRYPT: + if (g_need_passphrase) + get_passphrase(); + a_changed = TRUE; + break; + + case MC_SIGN: + state->next_screen = smime_info_screen; + break; +#endif + /*------- Bounce -----------*/ case MC_BOUNCE : cmd_bounce(state, msgmap, 0); diff -ruN original.pine4.61.sources/pine4.61/pine/mailview.c smime.pine.for.Linux/pine4.61/pine/mailview.c --- original.pine4.61.sources/pine4.61/pine/mailview.c Thu Jul 15 02:25:10 2004 +++ smime.pine.for.Linux/pine4.61/pine/mailview.c Fri Jul 16 23:17:46 2004 @@ -48,6 +48,9 @@ #include "headers.h" +#ifdef SMIME +#include "smime.h" +#endif /*---------------------------------------------------------------------- Saved state for scrolling text @@ -235,8 +238,13 @@ HOMEKEY_MENU, ENDKEY_MENU, RCOMPOSE_MENU, +#ifdef SMIME + {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}}, + {"^E","Sign", {MC_SIGN,1,{ctrl('e')},KS_NONE}}, +#else NULL_MENU, NULL_MENU, +#endif NULL_MENU, NULL_MENU, NULL_MENU, @@ -252,6 +260,8 @@ #define BOUNCE_KEY 33 #define FLAG_KEY 34 #define VIEW_PIPE_KEY 35 +#define DECRYPT_KEY (VIEW_PIPE_KEY + 3) +#define SIGN_KEY (DECRYPT_KEY + 1) static struct key simple_text_keys[] = {HELP_MENU, @@ -404,7 +414,6 @@ change folders, when it's time for a different screen, or when there are no more messages available. ---*/ - void mail_view_screen(ps) struct pine *ps; @@ -431,7 +440,7 @@ * Check total to make sure there's something to view. Check it * inside the loop to make sure everything wasn't expunged while * we were viewing. If so, make sure we don't just come back. - */ + */ if(mn_get_total(ps->msgmap) <= 0L || !ps->mail_stream){ q_status_message(SM_ORDER, 0, 3, "No messages to read!"); ps->next_screen = mail_index_screen; @@ -466,6 +475,32 @@ else ps->unseen_in_view = !mc->seen; + flags = 0; + +#ifdef SMIME + if(ErrorLog) + free_ErrorLog(); + + init_ErrorLog(); + + /* Attempt to handle S/MIME bodies */ + /* we need original message */ + if(body->subtype && (!strucmp(body->subtype,"x-pkcs7-enclosure") || + !strucmp(body->subtype,"timestamp"))) + { + back_2_original(&body, ps->mail_stream, raw_msgno); + flags |= FM_NEW_MESS; + } + + + /* check it for signature, cr. ot time stamp */ + if(IS_ON(VERIFY_ON)) + { + if (fiddle_smime_message(body,raw_msgno,(flags&FM_NEW_MESS)!=0)) + flags |= FM_NEW_MESS; /* body was changed, force a reload */ + } +#endif + #if defined(DOS) && !defined(WIN32) /* * Handle big text for DOS here. @@ -493,7 +528,7 @@ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps) + SCROLL_LINES_BELOW(ps))); - flags = FM_DISPLAY; + flags |= FM_DISPLAY; if(last_message_viewed != mn_get_cur(ps->msgmap) || last_was_full_header == 2) flags |= FM_NEW_MESS; @@ -509,8 +544,8 @@ #ifdef _WINDOWS mswin_noscrollupdate(1); #endif - (void) format_message(raw_msgno, env, body, &handles, flags, - view_writec); + (void) format_message(raw_msgno, env, body, &handles, flags, view_writec); + #ifdef _WINDOWS mswin_noscrollupdate(0); #endif @@ -584,6 +619,15 @@ if(F_OFF(F_ENABLE_FULL_HDR, ps_global)) clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap); + +#ifdef SMIME + /* don't show MC_DECRYPT command */ + if(IS_ON(VERIFY_ON)) + { + if (!g_need_passphrase) + clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap); + } +#endif if(!handles){ /* @@ -807,7 +851,50 @@ } +#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") || + !strucmp(body->subtype,"timestamp")) || + is_timestamped(body)) + { + + 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 @@ -1772,6 +1859,25 @@ show_parts = 0; +#ifdef SMIME + /* decribe S/MIME parts */ + if(IS_ON(VERIFY_ON)) + { + if (flgs & FM_DISPLAY) { + + if (ErrorLog_NOT_EMPTY) + { + format_editorial("S/MIME error. Press \"^E\" for more details", ps_global->ttyo->screen_cols,pc); + gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + } + + 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++) { @@ -6910,8 +7016,6 @@ } - - /*---------------------------------------------------------------------- Format a strings describing one unshown part of a Mime message @@ -6961,6 +7065,36 @@ t = &tmp_20k_buf[strlen(tmp_20k_buf)]; +#ifdef SMIME + /* upps, something was wrong .. */ + if(IS_ON(VERIFY_ON) && is_pkcs7_body(body) && type!=3) + { + sstrcpy(&t,"\015\012"); + + /* PKCS7... */ + sstrcpy(&t, + "This part is a PKCS7 S/MIME enclosure. "); + + if(g_need_passphrase) + { + sstrcpy(&t, + "You may be able to view it by entering the correct passphrase " + "with the \"^D\" command."); + } + else + sstrcpy(&t,"'cause of error can't be displayed."); + + + } else + + /* time stamped part */ + if(IS_ON(VERIFY_ON) && (body->subtype && !strucmp(body->subtype,"x-timestamp")) && type != 3) + { + sstrcpy(&t,"\015\012"); + sstrcpy(&t,"This part is Time Stamped. 'cause of error can't be displayed."); + + } else +#endif if(type){ sstrcpy(&t, "\015\012"); switch(type) { diff -ruN original.pine4.61.sources/pine4.61/pine/makefile.cyg smime.pine.for.Linux/pine4.61/pine/makefile.cyg --- original.pine4.61.sources/pine4.61/pine/makefile.cyg Tue Nov 25 07:47:32 2003 +++ smime.pine.for.Linux/pine4.61/pine/makefile.cyg Thu Jul 15 13:03:23 2004 @@ -1,142 +1,154 @@ -# $Id: makefile.cyg,v 1.5 2003/11/25 06:46:01 jpf Exp $ -# -# T H E P I N E M A I L S Y S T E M -# -# Laurence Lundblade and Mike Seibel -# Networks and Distributed Computing -# Computing and Communications -# University of Washington -# Administration Building, AG-44 -# Seattle, Washington, 98195, USA -# Internet: lgl@CAC.Washington.EDU -# mikes@CAC.Washington.EDU -# -# Please address all bugs and comments to "pine-bugs@cac.washington.edu" -# -# -# Pine and Pico are registered trademarks of the University of Washington. -# No commercial use of these trademarks may be made without prior written -# permission of the University of Washington. -# -# Pine, Pico, and Pilot software and its included text are Copyright -# 1989-2002 by the University of Washington. -# -# The full text of our legal notices is contained in the file called -# CPYRIGHT, included with this distribution. -# -# -# Pine is in part based on The Elm Mail System: -# *********************************************************************** -# * The Elm Mail System - Revision: 2.13 * -# * * -# * Copyright (c) 1986, 1987 Dave Taylor * -# * Copyright (c) 1988, 1989 USENET Community Trust * -# *********************************************************************** -# -# - - -# -# Make file for the Pine mail system for Cygwin b20.1. -# -# Port maintainer: Eduardo Chappa -# -# Most commonly fiddled flags for compiler. -# Uncomment the setttings desired here -# -RM= rm -f -LN= ln -s -MAKE= make -OPTIMIZE= # -O2 -PROFILE= # -pg -DEBUG= -g -DDEBUG -DDEBUGJOURNAL - -CCLIENTDIR= ../c-client -PICODIR= ../pico - -# Only need to uncomment next two lines if you run make from this directory -# and you don't want to supply them as arguments to the make. -#LDAPLIBS= ../ldap/libraries/libldap.a ../ldap/libraries/liblber.a -#LDAPCFLAGS= -DENABLE_LDAP -I../ldap/include -# Object files that need to be rebuilt if ENABLE_LDAP gets defined. -LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ - other.o pine.o takeaddr.o - -STDLIBS= -ltermcap -LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a -LIBS= $(EXTRALIBES) $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ - `cat $(CCLIENTDIR)/LDFLAGS` - -STDCFLAGS= -DCYGWIN -DSYSTYPE=\"CYG\" -DMOUSE -CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ - $(LOCALPINECFLAGS) $(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 \ - $(PICODIR)/edef.h $(PICODIR)/efunc.h \ - $(PICODIR)/pico.h $(PICODIR)/os.h \ - $(CCLIENTDIR)/mail.h $(CCLIENTDIR)/osdep.h \ - $(CCLIENTDIR)/rfc822.h $(CCLIENTDIR)/misc.h - -all: pine rpdump rpload - -pine: $(OFILES) $(LOCLIBS) - echo "char datestamp[]="\"`date`\"";" > date.c - echo "char hoststamp[]="\"`hostname`\"";" >> date.c - $(CC) $(LDFLAGS) $(CFLAGS) -o pine $(OFILES) date.c $(LIBS) - -rpdump: rpdump.o $(CCLIENTDIR)/c-client.a - $(CC) $(LDFLAGS) $(CFLAGS) -o rpdump rpdump.o $(LIBS) - -rpload: rpload.o $(CCLIENTDIR)/c-client.a - $(CC) $(LDFLAGS) $(CFLAGS) -o rpload rpload.o $(LIBS) - -abookcpy: abookcpy.o $(LOCLIBES) - $(CC) $(LDFLAGS) $(CFLAGS) -o abookcpy abookcpy.o $(LIBS) - -pine-use: pine-use.c - $(CC) -o pine-use pine-use.c - -clean: - $(RM) *.o os.h os.c helptext.c helptext.h pine.exe rpload.exe rpdump.exe - cd osdep; make clean; cd .. - -os.h: osdep/os-cyg.h - $(RM) os.h - $(LN) osdep/os-cyg.h os.h - -os.c: osdep/os-cyg.c - $(RM) os.c - $(LN) osdep/os-cyg.c os.c - -$(OFILES): $(HFILES) -addrbook.o adrbkcmd.o adrbklib.o bldaddr.o takeaddr.o: adrbklib.h -context.o: $(CCLIENTDIR)/misc.h -send.o: $(CCLIENTDIR)/smtp.h -$(LDAPOFILES): $(LDAPLIBS) - -helptext.c: pine.hlp - ./cmplhelp.sh < pine.hlp > helptext.c - -helptext.h: pine.hlp - ./cmplhlp2.sh < pine.hlp > helptext.h - -osdep/os-cyg.c: osdep/bld_path osdep/canacces osdep/canonicl \ - osdep/chnge_pw osdep/coredump osdep/creatdir \ - osdep/diskquot.non osdep/domnames osdep/err_desc \ - osdep/expnfldr osdep/fgetpos.non osdep/filesize osdep/fltrname \ - osdep/fnexpand osdep/header osdep/hostname \ - osdep/jobcntrl osdep/lstcmpnt osdep/mimedisp osdep/pipe \ - osdep/print osdep/pw_stuff osdep/readfile osdep/debuging.tim \ - osdep/rename osdep/tempfile osdep/writ_dir \ - osdep/termin.unx osdep/termout.unx \ - osdep/termin.gen osdep/termout.gen \ - osdep/sendmail osdep/execview \ - osdep/postreap.wtp osdep/os-cyg.ic - cd osdep; $(MAKE) includer os-cyg.c; cd .. +# $Id: makefile.cyg,v 1.5 2003/11/25 06:46:01 jpf Exp $ +# +# T H E P I N E M A I L S Y S T E M +# +# Laurence Lundblade and Mike Seibel +# Networks and Distributed Computing +# Computing and Communications +# University of Washington +# Administration Building, AG-44 +# Seattle, Washington, 98195, USA +# Internet: lgl@CAC.Washington.EDU +# mikes@CAC.Washington.EDU +# +# Please address all bugs and comments to "pine-bugs@cac.washington.edu" +# +# +# Pine and Pico are registered trademarks of the University of Washington. +# No commercial use of these trademarks may be made without prior written +# permission of the University of Washington. +# +# Pine, Pico, and Pilot software and its included text are Copyright +# 1989-2002 by the University of Washington. +# +# The full text of our legal notices is contained in the file called +# CPYRIGHT, included with this distribution. +# +# +# Pine is in part based on The Elm Mail System: +# *********************************************************************** +# * The Elm Mail System - Revision: 2.13 * +# * * +# * Copyright (c) 1986, 1987 Dave Taylor * +# * Copyright (c) 1988, 1989 USENET Community Trust * +# *********************************************************************** +# +# + + +# +# Make file for the Pine mail system for Cygwin b20.1. +# +# Port maintainer: Eduardo Chappa +# +# Most commonly fiddled flags for compiler. +# Uncomment the setttings desired here +# +RM= rm -f +LN= ln -s +MAKE= make +OPTIMIZE= # -O2 +PROFILE= # -pg +DEBUG= -g -DDEBUG -DDEBUGJOURNAL + +CCLIENTDIR= ../c-client +PICODIR= ../pico + +# Only need to uncomment next two lines if you run make from this directory +# and you don't want to supply them as arguments to the make. +#LDAPLIBS= ../ldap/libraries/libldap.a ../ldap/libraries/liblber.a +#LDAPCFLAGS= -DENABLE_LDAP -I../ldap/include +# Object files that need to be rebuilt if ENABLE_LDAP gets defined. +LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ + other.o pine.o takeaddr.o + +SSLDIR= /home/xkouril/pine_openssl/openssl +SSLCERTS= $(SSLDIR)/certs +SSLINCLUDE= $(SSLDIR)/include +SSLLIB= $(SSLDIR)/lib + +SSLCFLAGS= -I$(SSLINCLUDE) \ + -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \ + -DSMIME +SSLLDFLAGS= -L$(SSLLIB) -lcrypto + +STDLIBS= -ltermcap +LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a +LIBS= $(EXTRALIBES) $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ + $(SSLLDFLAGS) \ + `cat $(CCLIENTDIR)/LDFLAGS` + +STDCFLAGS= -DCYGWIN -DSYSTYPE=\"CYG\" -DMOUSE +CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ + $(SSLCFLAGS) \ + $(LOCALPINECFLAGS) $(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 \ + $(PICODIR)/edef.h $(PICODIR)/efunc.h \ + $(PICODIR)/pico.h $(PICODIR)/os.h \ + $(CCLIENTDIR)/mail.h $(CCLIENTDIR)/osdep.h \ + $(CCLIENTDIR)/rfc822.h $(CCLIENTDIR)/misc.h + +all: pine rpdump rpload + +pine: $(OFILES) $(LOCLIBS) + echo "char datestamp[]="\"`date`\"";" > date.c + echo "char hoststamp[]="\"`hostname`\"";" >> date.c + $(CC) $(LDFLAGS) $(CFLAGS) -o pine $(OFILES) date.c $(LIBS) + +rpdump: rpdump.o $(CCLIENTDIR)/c-client.a + $(CC) $(LDFLAGS) $(CFLAGS) -o rpdump rpdump.o $(LIBS) + +rpload: rpload.o $(CCLIENTDIR)/c-client.a + $(CC) $(LDFLAGS) $(CFLAGS) -o rpload rpload.o $(LIBS) + +abookcpy: abookcpy.o $(LOCLIBES) + $(CC) $(LDFLAGS) $(CFLAGS) -o abookcpy abookcpy.o $(LIBS) + +pine-use: pine-use.c + $(CC) -o pine-use pine-use.c + +clean: + $(RM) *.o os.h os.c helptext.c helptext.h pine.exe rpload.exe rpdump.exe + cd osdep; make clean; cd .. + +os.h: osdep/os-cyg.h + $(RM) os.h + $(LN) osdep/os-cyg.h os.h + +os.c: osdep/os-cyg.c + $(RM) os.c + $(LN) osdep/os-cyg.c os.c + +$(OFILES): $(HFILES) +addrbook.o adrbkcmd.o adrbklib.o bldaddr.o takeaddr.o: adrbklib.h +context.o: $(CCLIENTDIR)/misc.h +send.o: $(CCLIENTDIR)/smtp.h +$(LDAPOFILES): $(LDAPLIBS) + +helptext.c: pine.hlp + ./cmplhelp.sh < pine.hlp > helptext.c + +helptext.h: pine.hlp + ./cmplhlp2.sh < pine.hlp > helptext.h + +osdep/os-cyg.c: osdep/bld_path osdep/canacces osdep/canonicl \ + osdep/chnge_pw osdep/coredump osdep/creatdir \ + osdep/diskquot.non osdep/domnames osdep/err_desc \ + osdep/expnfldr osdep/fgetpos.non osdep/filesize osdep/fltrname \ + osdep/fnexpand osdep/header osdep/hostname \ + osdep/jobcntrl osdep/lstcmpnt osdep/mimedisp osdep/pipe \ + osdep/print osdep/pw_stuff osdep/readfile osdep/debuging.tim \ + osdep/rename osdep/tempfile osdep/writ_dir \ + osdep/termin.unx osdep/termout.unx \ + osdep/termin.gen osdep/termout.gen \ + osdep/sendmail osdep/execview \ + osdep/postreap.wtp osdep/os-cyg.ic + cd osdep; $(MAKE) includer os-cyg.c; cd .. diff -ruN original.pine4.61.sources/pine4.61/pine/makefile.lnx smime.pine.for.Linux/pine4.61/pine/makefile.lnx --- original.pine4.61.sources/pine4.61/pine/makefile.lnx Tue Nov 25 07:47:42 2003 +++ smime.pine.for.Linux/pine4.61/pine/makefile.lnx Fri Jul 16 23:17:46 2004 @@ -60,20 +60,33 @@ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ mailview.o other.o pine.o strings.o takeaddr.o +SSLDIR= /home/xkouril/pine_openssl/openssl +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) \ $(LOCALPINECFLAGS) $(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 + 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,3 +148,7 @@ 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 -ruN original.pine4.61.sources/pine4.61/pine/makefile.osf smime.pine.for.Linux/pine4.61/pine/makefile.osf --- original.pine4.61.sources/pine4.61/pine/makefile.osf Tue Nov 25 07:48:04 2003 +++ smime.pine.for.Linux/pine4.61/pine/makefile.osf Fri Jul 16 23:17:46 2004 @@ -64,17 +64,30 @@ STDLIBS= -lcurses LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ + $(SSLLDFLAGS) \ `cat $(CCLIENTDIR)/LDFLAGS` +SSLDIR= /home/xkouril/pine_openssl/openssl +SSLCERTS= $(SSLDIR)/certs +SSLINCLUDE= $(SSLDIR)/include +SSLLIB= $(SSLDIR)/lib + +SSLCFLAGS= -I$(SSLINCLUDE) \ + -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \ + -DSMIME +SSLLDFLAGS= -L$(SSLLIB) -lcrypto + + STDCFLAGS= -D_BSD -DSYSTYPE=\"OSF\" -DMOUSE CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ + $(SSLCFLAGS) \ $(LOCALPINECFLAGS) $(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 + 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 -ruN original.pine4.61.sources/pine4.61/pine/makefile.so5 smime.pine.for.Linux/pine4.61/pine/makefile.so5 --- original.pine4.61.sources/pine4.61/pine/makefile.so5 Sat Apr 3 01:16:17 2004 +++ smime.pine.for.Linux/pine4.61/pine/makefile.so5 Fri Jul 16 23:17:46 2004 @@ -62,6 +62,17 @@ 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,17 +83,19 @@ 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) \ $(LOCALPINECFLAGS) $(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 + 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 -ruN original.pine4.61.sources/pine4.61/pine/other.c smime.pine.for.Linux/pine4.61/pine/other.c --- original.pine4.61.sources/pine4.61/pine/other.c Fri Jul 2 23:38:16 2004 +++ smime.pine.for.Linux/pine4.61/pine/other.c Fri Jul 16 23:17:46 2004 @@ -50,6 +50,10 @@ #include "headers.h" #include "adrbklib.h" +#ifdef SMIME +#include "smime.h" +#endif + extern PAT_HANDLE **cur_pat_h; #define BODY_LINES(X) ((X)->ttyo->screen_rows -HEADER_ROWS(X)-FOOTER_ROWS(X)) @@ -1385,8 +1389,17 @@ add_hidden_vars_title = 1; if(exclude_config_var(ps, vtmp, expose_hidden_config)) - continue; + continue; +#ifdef SMIME + /* insert blank line */ + if(vtmp == &ps->vars[V_CAPATH]) + { + new_confline(&ctmpa); + ctmpa->flags |= CF_NOSELECT | CF_B_LINE; + } +#endif + if(add_hidden_vars_title){ add_hidden_vars_title = 0; @@ -1478,6 +1491,63 @@ ctmpa->value = pretty_value(ps, ctmpa); } } +#ifdef SMIME + /* smime-options */ + else if(vtmp == &ps->vars[V_SMIME_OPTIONS]) + { + char buf[256]; + + ctmpa->flags |= CF_NOSELECT; + ctmpa->keymenu = &config_checkbox_keymenu; + ctmpa->tool = NULL; + + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_checkbox_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = checkbox_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("Set Option Name"); + + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_checkbox_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = checkbox_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("--- ----------------------"); + + for(i = 0; i < SMIME_OPTIONS; i++) + { + new_confline(&ctmpa)->var = vtmp; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_checkbox_keymenu; + ctmpa->help = h_smime_help; + ctmpa->tool = checkbox_tool; + ctmpa->varmem = i; + ctmpa->valoffset = 12; + if((!strucmp(smime_options[i].name,"crl-check") || + !strucmp(smime_options[i].name,"crl-check-all") || + !strucmp(smime_options[i].name,"ocsp-check"))) + { + if(IS_OFF(VERIFY_ON)) + ctmpa->flags |= CF_NOSELECT; + } + else if(!strucmp(smime_options[i].name, "crls-from-CAs")) + { + if(IS_OFF(VERIFY_ON) || + (IS_ON(VERIFY_ON) && IS_OFF(CRL_CHECK) && IS_OFF(CRL_CHECK_ALL))) + ctmpa->flags |= CF_NOSELECT; + } + + sprintf(buf,"[%s] %s",IS_ON(smime_options[i].value) ? "X" : " ",smime_options[i].name); + ctmpa->value = cpystr(buf); + } + } +#endif + else if(standard_radio_var(ps, vtmp)){ standard_radio_setup(ps, &ctmpa, vtmp, NULL); } @@ -7268,6 +7338,15 @@ return(feature_list_help(feature)); break; +#ifdef SMIME + /* go to S/MIME help*/ + case V_PUBLIC : + case V_CAPATH : + case V_KEY_CERT_PATH : + case V_CERTFILE : + case V_TSA : + return(h_smime_help); +#endif case V_PERSONAL_NAME : return(h_config_pers_name); case V_USER_ID : @@ -7819,6 +7898,15 @@ HelpType help; ESCKEY_S ekey[6]; + +#ifdef SMIME + /* reload private data*/ + if((*cl)->var == &ps->vars[V_KEY_CERT_PATH] || + (*cl)->var == &ps->vars[V_PERSONAL_NAME] || + (*cl)->var == &ps->vars[V_USER_DOMAIN]) + Openssl_init |= RELOAD_MY_ALL; +#endif + if((*cl)->var->is_list){ lval = LVAL((*cl)->var, ew); alval = ALVAL((*cl)->var, ew); @@ -8935,6 +9023,71 @@ } } +#ifdef SMIME + +#define cmpustr(i,str) strucmp(smime_options[i].name, str) +/*--- + Mark/Unmark smime/options +Args: current - + index - + cl - +Result: - +---*/ +void change_smime_options(char ***current, int index, CONF_S *cl) +{ + char **current_here; + char **main_user_here; + int count = 0, i; + + CONF_S *var = NULL; + + smime_flags = IS_ON(smime_options[index].value) ? + smime_flags & ~smime_options[index].value : smime_flags | smime_options[index].value; + + if(!cmpustr(index,"verify-on")) + for(var = cl->next; var != NULL; var = var->next) + if(strstr(var->value,"crl-check") || strstr(var->value,"crl-check-all") || + strstr(var->value,"ocsp-check") || strstr(var->value,"crls-from-CAs")) + { + if(strstr(var->value,"crls-from-CAs")) + { + if(IS_ON(VERIFY_ON) && (IS_ON(CRL_CHECK) || IS_ON(CRL_CHECK_ALL))) + var->flags &= ~CF_NOSELECT; + else + var->flags |= CF_NOSELECT; + } + else if(IS_ON(VERIFY_ON)) + var->flags &= ~CF_NOSELECT; + else + var->flags |= CF_NOSELECT; + } + + if(!cmpustr(index,"crl-check-all") || !cmpustr(index,"crl-check")) + for(var = cl->next; var != NULL; var = var->next) + if(strstr(var->value,"crls-from-CAs")) + { + if(IS_ON(CRL_CHECK) || IS_ON(CRL_CHECK_ALL)) + var->flags &= ~CF_NOSELECT; + else + var->flags |= CF_NOSELECT; + break; + } + + if(current && *current) + fs_give((void **)*current); + for(i = 0; i < SMIME_OPTIONS; i++) + if(IS_ON(smime_options[i].value)) count++; + *current = (char **)fs_get((count+1) * (sizeof(char *))); + current_here = *current; + count = 0; + for(i = 0; i < SMIME_OPTIONS; i++) + if(IS_ON(smime_options[i].value)) + current_here[count++] = cpystr(smime_options[i].name); + current_here[count] = NULL; + if(cl->value && cl->value[0]) + cl->value[1] = (cl->value[1] == ' ') ? 'X' : ' '; +} +#endif /* * feature list manipulation tool @@ -8957,7 +9110,16 @@ rv = 1; toggle_feature_bit(ps, (*cl)->varmem, (*cl)->var, *cl, 0); } - else + else + +#ifdef SMIME + if((*cl)->var == &ps->vars[V_SMIME_OPTIONS]) + { + rv = 1; + /* mark/inmark smime-option */ + change_smime_options(&ps->vars[V_SMIME_OPTIONS].main_user_val.l,(*cl)->varmem, *cl); + } else +#endif q_status_message(SM_ORDER | SM_DING, 3, 6, "Programmer botch! Unknown checkbox type."); diff -ruN original.pine4.61.sources/pine4.61/pine/pine.h smime.pine.for.Linux/pine4.61/pine/pine.h --- original.pine4.61.sources/pine4.61/pine/pine.h Thu Jul 15 02:25:12 2004 +++ smime.pine.for.Linux/pine4.61/pine/pine.h Fri Jul 16 23:17:46 2004 @@ -671,7 +671,17 @@ , V_MAXREMSTREAM , V_PERMLOCKED , V_DEADLETS - /* +#ifdef SMIME + /* S/MIME options */ + , V_CAPATH + , V_KEY_CERT_PATH + , V_PUBLIC + , V_CERTFILE + , V_TSA + , V_SMIME_OPTIONS +#endif + +/* * Starting here, the rest of the variables are hidden by default in config * screen. They are exposed with expose-hidden-config feature. */ @@ -903,6 +913,19 @@ #define GLO_BUGS_ADDRESS vars[V_BUGS_ADDRESS].global_val.p #define VAR_BUGS_EXTRAS vars[V_BUGS_EXTRAS].current_val.p #define GLO_BUGS_EXTRAS vars[V_BUGS_EXTRAS].global_val.p + +#ifdef SMIME +#define VAR_CAPATH vars[V_CAPATH].current_val.p +#define GLO_CAPATH vars[V_CAPATH].global_val.p +#define VAR_KEY_CERT_PATH vars[V_KEY_CERT_PATH].current_val.p +#define GLO_KEY_CERT_PATH vars[V_KEY_CERT_PATH].global_val.p +#define VAR_SMIME_OPTIONS vars[V_SMIME_OPTIONS].current_val.l +#define VAR_PUBLIC vars[V_PUBLIC].current_val.p +#define GLO_PUBLIC vars[V_PUBLIC].global_val.p +#define VAR_CERTFILE vars[V_CERTFILE].current_val.p +#define VAR_TSA vars[V_TSA].current_val.p +#endif + #define VAR_SUGGEST_FULLNAME vars[V_SUGGEST_FULLNAME].current_val.p #define GLO_SUGGEST_FULLNAME vars[V_SUGGEST_FULLNAME].global_val.p #define VAR_SUGGEST_ADDRESS vars[V_SUGGEST_ADDRESS].current_val.p @@ -2616,6 +2639,8 @@ } cmdline_val; /* user typed as cmdline arg */ }; +#define MC_DECRYPT 800 +#define MC_SIGN 801 /* @@ -5113,6 +5138,13 @@ char *pine_send_status PROTO((int, char *, char *, int *)); void phone_home PROTO((char *)); void pine_free_body PROTO((BODY **)); + +#ifdef SMIME +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 *)); +#endif + 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 -ruN original.pine4.61.sources/pine4.61/pine/pine.hlp smime.pine.for.Linux/pine4.61/pine/pine.hlp --- original.pine4.61.sources/pine4.61/pine/pine.hlp Thu Jul 15 19:37:22 2004 +++ smime.pine.for.Linux/pine4.61/pine/pine.hlp Fri Jul 16 23:17:46 2004 @@ -1424,6 +1424,7 @@
  • Securing Your Pine Session
  • Reporting Problems
  • Show Available Options +
  • S/MIME patch
  • Index to Pine's Online Help @@ -3448,6 +3449,7 @@
  • Show Available Options
  • Signature Editor Commands Explained
  • Simple Text View Screen Explained +
  • S/MIME patch
  • Sort Command
  • Spell Check Command
  • Status Line @@ -20125,6 +20127,66 @@ <End of help on this topic> +====== h_smime_help ===== + + +

    S/MIME patch

    + +This patch enables S/MIME support. Original patch from Jonathan Paisley was touched up to support certificate status verification using CRL & OCSP, time stamped messages and to include S/MIME options and this short S/MIME help. Some bugs was removed too. + +

    +For more details read "pine/README.smime". Known bugs are in "pine/TODO.smime". + +

    SMIME/options

    + +

    Ca-dir

    +This value overrides the default value of directory with trusted CA certificates and theit CRLs. Default value is "~/.pine-smime/ca". We use this documents to verify signatures - incoming sign messages, TSA Time stamp response, OCSP repsonse. Documents should be in OpenSSL standart hashed format. + +

    Private-dir

    +This value overrides the default value of directory with private key(s) and certificate(s). We use them to decrypt incoming or sign outgoing messages. Default value is "~/.pine-smime/private". + +

    Public-dir

    +This value overrides the default value of directory with other certificates. We use them to encrypt outgoing messages. Default value is "~/.pine-smime/public". + +

    Certfile

    +Allows to be specified a file with additional certificates and CRLs. When signing these will be included with the outgoing message. + +

    TSA

    +This value sets Time Stamp Authority URL where to send time stamp requests. Both HTTP and HTTPS (SSL/TLS) URLs can be specified. + +

    smime-options:verify-on

    +This feature sets/unsets S/MIME verification - if signature, encryption or time stamp is included in incoming message. + +

    smime-options:crl-check

    +This feature sets/unsets certificate status verification against local CRLs. Just signers certificates are verified. + +

    smime-options:crl-check-all

    +This feature sets/unsets certificate status verification against local CRLs. All certificates in verification chain are checked. + +

    smime-options:crls-from-CAs

    +This feature sets/unsets attempt to get CRL directly from CA URL(s) specified in certificate "CRL Distribution Points" extension, if this is missing or has expired. + +

    smime-options:ocsp-check

    +This feature sets/unsets certificate OCSP status verification. OCSP requests are sent to URL(s) specified in certificate "Authority Access Extension" extension. Both HTTP and HTTPS (SSL/TLS) URLs can be specified. + +

    smime-options:use-SSL_CERT_DIRECTORY

    +This feature sets/unsets usage of CAs certificates and CRLs directory specified by SSL_CERT_DIRECTORY variable. + +

    smime-options:sign-default-on

    +

    smime-options:encrypt-default-on

    +

    smime-options:time-stamp-default-on

    +This features set/unset default signing, encrypting and time stamping of outgoing message. + +

    smime-options:remember-smime-passphrase

    +This feature remembers user's passphrase for current session. + +

    smime-options:save-certs-and-crls

    +This feature saves certificates and CRLs. Certificates are saved into the public directory, CRLs into the ca directory. + +

    +<End of help on this topic> + + ====== h_config_alt_addresses ===== @@ -29383,3 +29445,10 @@ ========== 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 -ruN original.pine4.61.sources/pine4.61/pine/send.c smime.pine.for.Linux/pine4.61/pine/send.c --- original.pine4.61.sources/pine4.61/pine/send.c Wed Jun 30 20:28:02 2004 +++ smime.pine.for.Linux/pine4.61/pine/send.c Fri Jul 16 23:17:46 2004 @@ -51,6 +51,7 @@ #include "../c-client/nntp.h" #include "../c-client/imap4r1.h" +#include "smime.h" #ifndef TCPSTREAM #define TCPSTREAM void @@ -5916,6 +5917,30 @@ 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"; + + + opts[i].ch = 't'; + opts[i].rval = 't'; + opts[i].name = "T"; + opts[i++].label = "Time Stamp"; + + g_do_timestamp = IS_ON(TS_DEFAULT_ON); + g_do_encrypt = IS_ON(ENC_DEFAULT_ON); + g_do_sign = IS_ON(SIGN_DEFAULT_ON); + } +#endif + opts[i].ch = -1; fix_windsize(ps_global); @@ -6013,6 +6038,53 @@ 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"); + } + + if(g_do_timestamp) + { + if(!lparen) + { + *optp++ = ' '; + *optp++ = '('; + lparen++; + } + else + { + *optp++ = ','; + *optp++ = ' '; + } + + sstrcpy(&optp, "Time Stamped"); + } +#endif + if(lparen) *optp++ = ')'; @@ -6138,6 +6210,16 @@ * 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; + } + else if (rv == 't') + { + g_do_timestamp = !g_do_timestamp; +#endif } sprintf(dsn_string, "DSN requested[%s%s%s%s]", @@ -6915,6 +6997,7 @@ char *verbose_file = NULL; BODY *bp = NULL; PINEFIELD *pf; + BODY *origBody = body; #define MAX_ADDR_ERROR 2 /* Only display 2 address errors */ @@ -6931,6 +7014,48 @@ return(0); } + +#ifdef SMIME + if(g_do_encrypt || g_do_sign || g_do_timestamp) + { + int result; + + STORE_S *so = lmc.so; + lmc.so = NULL; + + result = 1; + + /* reload init */ + Openssl_init &= ~ALL_DONE; + + /* (re)init ErrorLog */ + if(ErrorLog) + free_ErrorLog(); + + init_ErrorLog(); + + 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); + + if(result && g_do_timestamp) + result = time_stamp_outgoing_message(&body); + + lmc.so = so; + + /* show prompt, info_screen with details */ + if (!result) + { + if (want_to("S/MIME posting error. Show details? ",'y','n',NO_HELP,WT_NORM) == 'y') + smime_info_screen(NULL, TYPE_ERR_OUT); + 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 */ @@ -7240,6 +7365,24 @@ 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); @@ -9347,13 +9490,17 @@ 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? */ + 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 ()); - body->parameter = mail_newbody_parameter (); - body->parameter->attribute = cpystr ("BOUNDARY"); - body->parameter->value = cpystr (tmp); + 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 -ruN original.pine4.61.sources/pine4.61/pine/smime.c smime.pine.for.Linux/pine4.61/pine/smime.c --- original.pine4.61.sources/pine4.61/pine/smime.c Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/smime.c Fri Jul 16 23:17:46 2004 @@ -0,0 +1,4264 @@ +/* + File: smime.c + Author: paisleyj@dcs.gla.ac.uk + +kouril.martin@seznam.cz + Date: 01/2001 + +01/2004 + + + 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() + + new misssing ! + */ + +#ifdef SMIME + +#include "headers.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "bss_so.h" +#include "smkeys.h" +#include "smime.h" +#include "smime.errors.h" + +int Openssl_init = RELOAD_MY_ALL; +int smime_flags; /* smime-options option */ + +#define TIME_STAMPED struct time_stamped +TIME_STAMPED { + ASN1_GENERALIZEDTIME *time; + TIME_STAMPED *next; +}; +TIME_STAMPED *TS_stack = NULL; /* time stamp stack */ +ERROR_LOG *ErrorLog; + +int g_need_passphrase = 0; +static int s_entered_passphrase = 0; +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; +int g_do_timestamp; + +static BIO *bio_err; + +/* Linked list of PERSONAL_CERT objects */ +static X509 *my_certificate; +static EVP_PKEY *my_key; + + +static X509_STORE *s_cert_store; + +/* State management for randomness functions below */ +static int seeded = 0; +static int egdsocket = 0; + + +void ErrorLog_push(const char* format, ...) +{ + va_list args; + char buf[MAXPATH]; + EXTENSION_STACK **stack = NULL; + + va_start(args, format); + vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + + switch (ErrorLog->err_no) + { + case MODE_SIGNATURE : + stack = &ErrorLog->signature; + break; + case MODE_TIME_STAMP : + stack = &ErrorLog->time_stamp; + break; + case MODE_CIPHER : + stack = &ErrorLog->cipher; + break; + default : + stack = &ErrorLog->glob; + } + + EXTENSION_STACK_push(buf, stack); +} + + +/* Forget any cached private keys */ +static void forget_private_keys() +{ + if (my_key) + { + EVP_PKEY_free(my_key); + my_key = NULL; + } +} + +static void forget_pcert() +{ + if(my_certificate) + { + X509_free(my_certificate); + my_certificate = 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) +{ + if (Openssl_init & ALL_DONE) + return; + + Openssl_init |= ALL_DONE; + + + if (!(Openssl_init & OPENSSL_INITED)) { + 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); + + Openssl_init |= OPENSSL_INITED; + } + + ERR_clear_error(); + + if (s_cert_store != NULL) + X509_STORE_free(s_cert_store); + + /* load trusted CAs certs nd CRLs directories */ + s_cert_store = get_ca_store(); + + if(Openssl_init & RELOAD_MY_CERTIFICATE) + { + forget_pcert(); + my_certificate = get_my_certificate(); + + if(my_certificate) + Openssl_init &= ~RELOAD_MY_CERTIFICATE; + } + + if(Openssl_init & RELOAD_MY_KEY) + { + forget_private_keys(); + my_key = get_my_key(); + Openssl_init &= ~RELOAD_MY_KEY; + } + +} + +/* 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 (NOT_NULL_NOT_0(errs)) + return errs; + else if (NOT_NULL_NOT_0(data)) + return data; + + return ""; +} + +/* 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; + + /* 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); + + 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; +} + +/* 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() +{ + if (!my_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(!(my_key = load_key(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)) + ErrorLog_push(HR_INCORRECT_PASS); + else + ErrorLog_push(HR_UNABLE_READ_KEY); + + + /* 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(my_certificate); + 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); +} + +/* + 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; + BIO *in = NULL; + BIO *out = NULL; + PKCS7 *p7 = NULL; + int result = 0; + PARAMETER *param; + + int flags = dont_detach ? 0 : PKCS7_DETACHED; + STACK_OF(X509) *certfile = NULL; + STACK_OF(X509_CRL) *crls = NULL; + + openssl_init(); + ErrorLog->err_no = MODE_SIGNATURE; + + /* forget private keys if is remember-smime-passphrase ON */ + if(IS_OFF(REM_SMIME_PSWD)) + forget_private_keys(); + + store = body_to_store(body); + + if (!my_certificate) + { + char *path_2_key = generate_file_path(MODE_PRIVATE, TYPE_CERT, who_am_I()); + + ErrorLog_push(HR_NO_PRIVATE_CERT, path_2_key ? path_2_key : "" ); + goto end; + } + + if (!load_private_key() && g_need_passphrase) { + /* Couldn't load key with blank password, try again */ + get_passphrase(); + load_private_key(); + } + + if (!my_key) + goto end; + + in = BIO_new_so(store); + + BIO_reset(in); + + /* use certfile from certfile option */ + if (NOT_NULL_NOT_0(ps_global->VAR_CERTFILE)) + if(!load_certfile(ps_global->VAR_CERTFILE, &certfile, &crls)) + { + ErrorLog_push(HR_CERTFILE,ps_global->VAR_CERTFILE, " "); + goto end; + } + + p7 = PKCS7_sign(my_certificate, my_key, certfile, in, flags); + if(p7) + /* paste CRLs to outgoing message */ + p7->d.sign->crl = crls; + else + { + ErrorLog_push(HR_UNABLE_LOAD_PKCS7,"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: + ErrorLog->err_no = MODE_GLOB; + 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(); + + ErrorLog->err_no = MODE_CIPHER; + cipher = (EVP_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(buf,MODE_PUBLIC); /* get cert for encryct */ + + if (cert) + { + sk_X509_push(encerts,cert); + } + else { + ErrorLog_push(HR_NO_PUBLIC_CERT, buf, ps_global->VAR_PUBLIC); + 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: + + ErrorLog->err_no = MODE_GLOB; + 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,0); + + 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; +} + +/* + Look for next issuer's cert(ctx->untrusted) +Args: ctx - current verify context + sk - untusted certificates store + x - issuers cert. +Result: NULL or next issuer's cert. +---*/ +static X509 *CRL_find_issuer(ctx, sk, x) + X509_STORE_CTX *ctx; + STACK_OF(X509) *sk; + X509 *x; +{ + int i; + X509 *issuer; + + for (i = 0; i < sk_X509_num(sk); i++) + { + issuer = sk_X509_value(sk, i); + + /* compare it */ + if (ctx->check_issued(ctx, x, issuer)) + return issuer; + } + return NULL; +} + +/*--- + Build verification chain for status verificaton against Local CRLs. + Taken from crypto/x509/x509_vfy.c. +Args: ctx - verification context +Result: 1/0 +---*/ +int CRL_build_chain(ctx) + X509_STORE_CTX *ctx; +{ + int ok = 0, depth, num; + STACK_OF(X509) *sktmp=NULL; + X509 *x, *xtmp; + + if(ctx->chain != NULL) + sk_X509_free(ctx->chain); + + if (!(ctx->chain=sk_X509_new_null())) + goto end; + + /* use untrusted certs */ + if (ctx->untrusted != NULL && (sktmp=sk_X509_dup(ctx->untrusted)) == NULL) + goto end; + + depth = ctx->depth - 1; + x = ctx->cert; + num = 0; + + /* start with untrusted certs */ + for(;;) + { + if(depth < num) + break; + if (ctx->check_issued(ctx, x,x)) + { + ok = 1; + goto end; + } + if (ctx->untrusted != NULL) + { + xtmp=CRL_find_issuer(ctx, sktmp,x); + if(xtmp != NULL) + { + if (!sk_X509_push(ctx->chain,xtmp)) + goto end; + CRYPTO_add(&xtmp->references,1,CRYPTO_LOCK_X509); + sk_X509_delete_ptr(sktmp,xtmp); + x=xtmp; + num++; + continue; + } + } + break; + } + /* continue with trusted CAs */ + for(;;) + { + if (depth < num) break; + if (ctx->check_issued(ctx,x,x)) + { + ok = 1; + goto end; + } + ok = ctx->get_issuer(&xtmp, ctx, x); + if (ok < 0) return 0; + if (ok == 0) break; + x = xtmp; + if (!sk_X509_push(ctx->chain,x)) + { + X509_free(xtmp); + goto end; + } + num++; + } + + ok = 1; +end: + if(sktmp) + sk_X509_free(sktmp); + return ok; + +} + +void EXTENSION_STACK_pop_all(stack) + EXTENSION_STACK **stack; +{ + if(*stack) + { + if((*stack)->value) + fs_give((void **) &(*stack)->value); + EXTENSION_STACK_pop_all( &(*stack)->next); + fs_give((void **) stack); + } +} + +int EXTENSION_STACK_push(new_top, stack) + char *new_top; + EXTENSION_STACK **stack; +{ + EXTENSION_STACK *new = NULL; + + new = (EXTENSION_STACK *)malloc(sizeof(EXTENSION_STACK)); + if (new) + { + new-> value = cpystr(new_top); + new->next = *stack; + *stack = new; + + return 1; + } + + return 0; +} + +char *convert_id(lvalue) + char *lvalue; +{ + if(!lvalue) + return ""; + else if(!strucmp(lvalue,"C")) + return "Country"; + else if(!strucmp(lvalue,"CN")) + return "Common Name"; + else if(!strucmp(lvalue,"GN")) + return "Given Name"; + else if(!strucmp(lvalue,"SN")) + return "Surname"; + else if(!strucmp(lvalue,"L")) + return "Locality"; + else if(!strucmp(lvalue,"ST")) + return "State or Province"; + else if(!strucmp(lvalue,"O")) + return "Organization"; + else if(!strucmp(lvalue,"OU")) + return "Organization unit"; + else if(!strucmp(lvalue,"serialNumber")) + return "Serial Number"; + else if(!strucmp(lvalue,"emailAddress")) + return "Email"; + else return lvalue; +} + +char *x_X509_NAME_oneline(X509_NAME *get_something_name(X509 *) , X509 *cert) +{ + + X509_NAME *name = get_something_name(cert); + int i = X509_NAME_entry_count(name), n; + char buf[MAXPATH]; + char rvalue[256]; + char all[256]; + + + if(--i < 0) + { + snprintf(buf, sizeof buf, "\r\n%-19s", ""); + goto end; + } + + buf[0] = '\0'; + for(; i >= 0; i--) + { + X509_NAME_ENTRY *e = X509_NAME_get_entry(name, i); + const char *lvalue; + + if(!e) + continue; + + n = OBJ_obj2nid(e->object); + lvalue = OBJ_nid2sn(n); + + X509_NAME_get_text_by_OBJ(name, e->object,rvalue,sizeof(rvalue)); + snprintf(all, sizeof all,"\r\n%-19s : %s", convert_id(lvalue), rvalue); + strncat(buf, all, MAXPATH - (strlen(buf) + strlen(all))); + } + +end: + return cpystr(buf); +} + + +char *x_X509_NAME_common_name(X509_NAME *get_something_name(X509 *) , X509 *cert) +{ + + X509_NAME *name = get_something_name(cert); + int i = X509_NAME_entry_count(name), n; + char buf[MAXPATH]; + char rvalue[256]; + char all[256]; + + + if(--i < 0) + { + snprintf(buf, sizeof buf, "%s", ""); + goto end; + } + + buf[0] = '\0'; + for(; i >= 0; i--) + { + X509_NAME_ENTRY *e = X509_NAME_get_entry(name, i); + const char *lvalue; + + if(!e) + continue; + + n = OBJ_obj2nid(e->object); + lvalue = OBJ_nid2sn(n); + + X509_NAME_get_text_by_OBJ(name, e->object,rvalue,sizeof(rvalue)); + if (memcmp(lvalue,"CN",2)==0) + { + snprintf(all, sizeof all,"%s", rvalue); + strncat(buf, all, MAXPATH - (strlen(buf) + strlen(all))); + } + } + +end: + return cpystr(buf); +} + + +EXTENSION_STACK *CRL_OCSP_get_extension_value(ctx, nid) + X509_STORE_CTX *ctx; + int nid; +{ + X509 *cert = ctx->cert; + int ext_nm, ok = 0, len; + X509_EXTENSION *ext = NULL; + BIO *bio = NULL; + EXTENSION_STACK *all = NULL, *new = NULL; + char buf[512]; + char *l_mode = nid == NID_info_access ? "OCSP" : "CRL"; + char *l_nid = nid == NID_info_access ? "\"Authority Access Info\"" : "\"CRL distribution poitns\""; + + /*get extension */ + ext_nm = X509v3_get_ext_by_NID(cert->cert_info->extensions , nid, -1); + if(!(ext = X509v3_get_ext(cert->cert_info->extensions, ext_nm))) + { + ErrorLog_push(HR_NO_EXTENSION, l_mode, l_nid, x_X509_NAME_oneline(X509_get_subject_name, cert)); + goto end; + } + + if(!(bio = BIO_new(BIO_s_mem()))) + { + ErrorLog_push(HR_CONNECTION_BIO, l_mode, "can't create"); + goto end; + } + + /* convert it to string */ + if(!X509V3_EXT_print(bio,ext,0,0)) + { + ErrorLog_push(HR_CONNECTION_BIO, l_mode, "can't parse extension"); + goto end; + } + + /* parse it */ + while((len = BIO_gets(bio, buf, sizeof buf)) > 0) + { + buf[len-1] = 0; + if(!EXTENSION_STACK_push(buf, &all)) + { + ErrorLog_push(HR_CONNECTION_BIO, l_mode, "can't parse extension"); + goto end; + } + } + + if(!all) + { + ErrorLog_push(HR_CONNECTION_BIO, l_mode, "can't parse extension"); + goto end; + } + + ok = 1; +end: + BIO_free(bio); + if(!ok) + { + EXTENSION_STACK_pop_all(&all); + all = NULL; + } + return all; +} + +/* --- + Create and setup connection BIO. +Args: cbio - connection BIO + nic - SSL context + host - remote host + port - remote port + path - remote path to document or service + use_ssl - +Result: NULL/ new connection BIO +---*/ + +int CRL_OCSP_connection_BIO_init(cbio, nic, host, port, path, use_ssl) + BIO **cbio; + SSL_CTX **nic; + char *host, *port, *path; + int use_ssl; +{ + int ok = 0; + + /* set remote host */ + if(!host) + { + ErrorLog_push(HR_CONNECTION_BIO,"No host" , ""); + goto end; + } +#ifndef OPENSSL_NO_SOCK + if(!(*cbio = BIO_new_connect(host))) + { + ErrorLog_push(HR_CONNECTION_BIO,"BIO_new_connect", ""); + goto end; + } +#else + ErrorLog_push(HR_CONNECTION_BIO,"Sockets not supported" , ""); + goto end; //Error creating connect BIO - sockets not supported +#endif + + /* usel ssl? */ + if(use_ssl == 1) + { + ErrorLog_push(HR_CONNECTION_BIO,"Httpd" , "not supported"); + goto end; + } + + /* connect to port */ + if (port) + BIO_set_conn_port(*cbio, port); + + ok = 1; +end: + return ok; +} + +/*--- + Read from connected BIO response and parse it. +Args: cbio - connection BIO +Result: if error occurs return NULL else wanted OCSP response/remote CRL. +---*/ +BIO *parsed_BIO_response(cbio, mode, host, port, path) + BIO *cbio; + char *mode, *host, *path, *port; +{ + BIO *mem = NULL; + char tmpbuf[1024]; + int len, ok = 0; + char *p, *q; + + if(!(mem = BIO_new(BIO_s_mem()))) + { + ErrorLog_push(HR_UNABLE_CREAT_BIO); + goto end; + } + + /* read response from BIO */ + while ((len = BIO_read(cbio, tmpbuf, sizeof tmpbuf))) + { + if(len < 0) + goto end; + + BIO_write(mem, tmpbuf, len); + +/* if(len < sizeof(tmpbuf)) + break; */ + } + /* parse it */ + if(BIO_gets(mem, tmpbuf, 512) <= 0) + { + len = -1; + goto end; + } + + for(p = tmpbuf; *p && !isspace((unsigned char)*p); p++) continue; + if(!*p) + { + len = -1; + goto end; + } + + while(*p && isspace((unsigned char)*p)) p++; + if(!*p) + { + len = -1; + goto end; + } + + /* have to be HTTP 200 OK... */ + if(strtoul(p, NULL, 10) != 200) + { + ErrorLog_push(HR_NOT_200_OK, mode, host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto end; + } + + /* find blank line */ + while(BIO_gets(mem, tmpbuf, 512) > 0) + { + for(p = tmpbuf; *p && isspace((unsigned char)*p); p++) continue; + if(!*p) break; + } + + if(*p) + { + len = -1; + goto end; + } + + ok = 1; +end: + if(!ok) + { + BIO_free(mem); + mem = NULL; + + if (len == -1) + ErrorLog_push(HR_UNABLE_PARSE_DATA); + } + + return mem; +} + +/*--- + Try to send HTTP request for application/x-pkcs7-crl (CRL), parse it and verify against + local issuer's cert. +Args: cbio - connection BIO + ctx - current verify context + path - path to remote CRL. + icert - issuers certificate +Result: return downloaded remote CRL or NULL +---*/ +X509_CRL *CRL_get_ol_CRL(cbio, ctx, host, port, path, icert) + BIO *cbio; + X509_STORE_CTX *ctx; + char *host, *port, *path; + X509 *icert; +{ + BIO *mem = NULL; + X509_CRL *x = NULL; + EVP_PKEY *ikey = NULL; + int ok = 0; + static char req_txt[] = +"GET %s HTTP/1.0\r\n" +"Accept: application/x-pkcs7-crl\r\n" +"\r\n"; + + if(BIO_printf(cbio, req_txt, path) < 0) + { + ErrorLog_push(HR_NO_RES, "CRL", host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto end; + } + + if(!(mem = parsed_BIO_response(cbio, "CRL", host, port, path))) + goto end; + + /* load CRL in PEM format */ + if(!(x = PEM_read_bio_X509_CRL(mem, NULL, NULL, NULL))) + { + ErrorLog_push(HR_UNABLE_READ_CRL); + goto end; + } + + /* get iss.public key bitstr */ + if(!(ikey = X509_get_pubkey(icert))) + { + ErrorLog_push(HR_UNABLE_DEC_ISS_PK, x_X509_NAME_oneline(X509_get_subject_name,icert)); + goto end; + } + + /* compare it */ + if(X509_CRL_verify(x, ikey) <= 0) + { + ErrorLog_push(HR_CERT_SIGN_FAIL,x_X509_NAME_oneline(X509_get_subject_name,icert)); + goto end; + } + + EVP_PKEY_free(ikey); + /* save */ + + ok = 1; +end: + BIO_free(mem); + if(!ok) + { + X509_CRL_free(x); + return NULL; + } + + return x; +} +/*--- + Try to download CRL from CA using HTTP(S). +Args: ctx - current verify context + icert - issuers certificate + ret - CRL will be saved here. +Result: 1/0 +---*/ + +int CRL_get_ol(ctx, icert, ret) + X509_STORE_CTX *ctx; + X509 *icert; + X509_OBJECT *ret; +{ + int ok = 0; + EXTENSION_STACK *crl_dp = NULL, *index; + X509_CRL *crl = NULL; + + /* get CRL distribition points extension */ + if(!(crl_dp = CRL_OCSP_get_extension_value(ctx, NID_crl_distribution_points))) + goto end; + + /* for all distribution points ...*/ + for(index = crl_dp; index; index = index->next) + { + SSL_CTX *nic = NULL; + BIO *cbio = NULL; + int use_ssl = -1; + char *host = NULL, *port = NULL, *path = "/"; + + /* parse it */ + if(!CRL_OCSP_parse_extension_value(1, index->value,&host, &port, &path, &use_ssl)) /* 1 == CRL mode */ + { + ErrorLog_push(HR_INV_URL, index->value); + goto for_end; + } + /* create connection BIO */ + if(!CRL_OCSP_connection_BIO_init(&cbio, &nic, host, port, path, use_ssl)) + goto for_end; + + /* do connect */ + if (BIO_do_connect(cbio) <= 0) + { + ErrorLog_push(HR_CONNECTION_FAILED, "OCSP", host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto for_end; + } + + /* get CRL */ + crl = CRL_get_ol_CRL(cbio, ctx, host, port, path, icert); + + for_end : + BIO_free_all(cbio); + if (use_ssl != -1) + { + OPENSSL_free(host); + OPENSSL_free(port); + OPENSSL_free(path); + SSL_CTX_free(nic); + } + if(crl) + break; + } + + if(!crl) + goto end; + + ret->data.crl = crl; + ok = 1; + +end: + EXTENSION_STACK_pop_all(&crl_dp); + + return ok; +} + +/*--- +? certificate was revoked -> try check against TS if it origins +before it's revocation. +Args: cert - revoke certificate + crl - CRL, we need it for revocation time of cert + i - +Result: 1/0 +---*/ + +int TIME_STAMPED_CRL_status_revoked(cert, crl, i) + X509 *cert; + X509_CRL *crl; + int i; +{ + ASN1_TIME *notBefore; + X509_REVOKED *revoked; + + if(!TS_stack) + return 0; + + notBefore = X509_get_notBefore(cert); + revoked = sk_X509_REVOKED_value(crl->crl->revoked, i); + + return (ASN1_TIME_vs_ASN1_TIME(notBefore, TS_stack->time) < 0 && + ASN1_TIME_vs_ASN1_TIME(TS_stack->time, revoked->revocationDate) < 0); +} + +/*--- +? local CRL expired, if is message time stamped try to check if it origins before CRL expired. +Args: notAfter - CRL's notAfter field +Result: 1/0 +---*/ + +int TIME_STAMPED_CRL_expired(notAfter) + ASN1_TIME *notAfter; +{ + return(TS_stack && ASN1_TIME_vs_ASN1_TIME(TS_stack->time, notAfter) < 0); +} + +/*--- + Verify status with CRL. If local CRL is missing or has expired try to + get CRL from URL in CRL distribution points extension. +Args: ctx - verify ctx +Result: 1/0 +---*/ +int CRL_status_verify(ctx) + X509_STORE_CTX *ctx; +{ + int ok = 0, j, depth, i; + X509 *cert; + + /* ? crl or crl-all options ON */ + if(IS_OFF(CRL_CHECK) && IS_OFF(CRL_CHECK_ALL)) + return 1; + + cert = ctx->cert; + + /* we need to build verification chain again */ + if(!CRL_build_chain(ctx)) + { + ErrorLog_push(HR_NO_VER_CHAIN); + goto end; + } + else if(!sk_X509_num(ctx->chain)) + { + ok = 1; + goto end; + } + + if(IS_ON(CRL_CHECK_ALL)) + depth = sk_X509_num(ctx->chain); + else + depth = 1; + + /*verify status against CAs' CRLs */ + for(j = 0; j < depth; j++) + { + X509 *issuer = NULL; + X509_NAME *iname = NULL; + X509_CRL *crl = NULL; + X509_OBJECT ret; + EVP_PKEY *ikey = NULL; + X509_REVOKED rtmp; + int crl_ol_was = 0; + char *iname_str = NULL; + + ok = 0; + + /* get issuer and his CRL */ + issuer = sk_X509_value(ctx->chain, j); + iname = issuer->cert_info->subject; + iname_str = x_X509_NAME_oneline(X509_get_issuer_name, cert); + + if(!X509_STORE_get_by_subject(ctx, X509_LU_CRL, iname, &ret)) + { + ErrorLog_push(HR_UNABLE_GET_CRL_FOR, iname_str); + +crl_ol: if(IS_ON(CRL_OL)) + { + /* no CRL or CRL expired , + try to get CRL from CA + */ + if(!CRL_get_ol(ctx, issuer, &ret)) + goto for_end; + + crl_ol_was = 1; + } + else + goto for_end; + } + + crl = ret.data.crl; + + /* compare public key bit str */ + if(!(ikey = X509_get_pubkey(issuer))) + { + ErrorLog_push(HR_UNABLE_DEC_ISS_PK, iname_str); + goto for_end; + } + + if(X509_verify(cert,ikey) <= 0) + { + ErrorLog_push(HR_CERT_SIGN_FAIL, iname_str); + goto for_end; + } + + if(X509_CRL_verify(crl, ikey) <= 0) + { + ErrorLog_push(HR_CRL_SIGN_FAIL, iname_str); + goto for_end; + } + EVP_PKEY_free(ikey); + + /* check CRL last update field */ + i = X509_cmp_time(X509_CRL_get_lastUpdate(crl), NULL); + if(!i) + { + ErrorLog_push(HR_INVALID_CRL_NBF, iname_str); + goto for_end; + } + else if (i > 0) + { + ErrorLog_push(HR_CRL_NOT_VALID_YET, iname_str); + goto for_end; + } + + /* check CRL next Update field */ + if(X509_CRL_get_nextUpdate(crl)) + { + ASN1_TIME *notAfter = X509_CRL_get_nextUpdate(crl); + + i = X509_cmp_time(notAfter, NULL); + if(!i) + { + ErrorLog_push(HR_INVALID_CRL_NAF, iname_str); + goto for_end; + } + /* if there is valid TS, check it*/ + else if(i < 0 && !TIME_STAMPED_CRL_expired(notAfter)) + { + ErrorLog_push(HR_CRL_EXPIRES, iname_str); + + if(crl_ol_was || IS_OFF(CRL_OL)) + goto for_end; + else + goto crl_ol; + } + } + /* if we get CRL from CA and everything is OK, save CRL*/ + if(i > 0 && crl_ol_was && IS_ON(SAVE_CERTS)) + CRL_save_ol_CRL(crl); + + rtmp.serialNumber = X509_get_serialNumber(cert); + + /*check status - revoked/valid */ + i = sk_X509_REVOKED_find(crl->crl->revoked, &rtmp); + + /* if revoked and there is TS, check it */ + if((i == -1) || TIME_STAMPED_CRL_status_revoked(cert, crl, i)) + ok = 1; + else + { + char *subject_str = x_X509_NAME_oneline(X509_get_subject_name, cert); + ErrorLog_push(HR_STATUS, "revoked", subject_str); + OPENSSL_free(subject_str); + } + + for_end: + cert = sk_X509_value(ctx->chain, j); + OPENSSL_free(iname_str); + if(!ok) break; + } + +end: + return ok; +} +/*--- + Parse Authority Access Info and CRL Distribution Points cert extensions. + Expected IP, URL HTTP(S). + +Args: type - OCSP_MODE/CRL_MODE + ext_value - + host - + port - + path - + use_ssl - +Result: 1/0 +---*/ + +int CRL_OCSP_parse_extension_value(type, ext_value, host, port, path, use_ssl) + int type; + const char *ext_value; + char **host, **port, **path; + int *use_ssl; +{ + char *vlm = NULL; + int ok = 0; + + /* if not OCSP_MODE, jump */ + if(type) + goto no_ocsp; + + if(strncmp(ext_value,"OCSP",4)) + goto end; + +no_ocsp: + if(vlm = strstr(ext_value,"URI:")) + { + vlm += 4; + if(!OCSP_parse_url(vlm, host, port, path, use_ssl)) + goto end; + } + else if(vlm = strstr(ext_value,"IP:")) + { + vlm +=3; + *host = cpystr(vlm); + } + + ok = 1; +end: + return ok; +} + +/*--- + If the message includes valid TS, it's possible to check + if certificate wasn't revoked after signing message. Compare revocation time + and TS time. +Args: rev - revocation time +Result: 1/0 +---*/ + +int TIME_STAMPED_OCSP_status_revoked(rev) + ASN1_GENERALIZEDTIME *rev; +{ + return (TS_stack && ASN1_TIME_vs_ASN1_TIME(TS_stack->time, rev) < 0); +} + +/*--- + Verify status with OCSP request if is ocsp option on and certifikate + includes Authority access info extension. +Args: ctx - verify context +Result: 1/0 +---*/ + +int OCSP_status_verify(ctx) + X509_STORE_CTX *ctx; +{ + int ok = 0; + X509_OBJECT ret; + X509 *issuer = NULL; + char *issuer_str = NULL; + X509_NAME *iname = NULL; + ASN1_BIT_STRING *ikeybitstr = NULL; + X509 *cert = NULL; + char *cert_str = NULL; + EVP_PKEY *ikey = NULL; + EXTENSION_STACK *aAI_str = NULL, *index; + OCSP_REQUEST *req = NULL; + OCSP_CERTID *id = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_BASICRESP *bs = NULL; + int k,status = -1; + long nsec = MAX_VALIDITY_PERIOD; + ASN1_GENERALIZEDTIME *rev = NULL, *thisupd = NULL, *nextupd = NULL; + + + if(IS_OFF(OCSP_CHECK)) + return 1; + + cert = ctx->cert; + + iname = cert->cert_info->issuer; + issuer_str = x_X509_NAME_oneline(X509_get_issuer_name, cert); + cert_str = x_X509_NAME_oneline(X509_get_subject_name, cert); + + /* we need issuer p.key bit string*/ + if(!X509_STORE_get_by_subject(ctx, X509_LU_X509 , iname, &ret)) + { + ErrorLog_push(HR_NO_ISSUER, issuer_str); + goto end; + } + + issuer = ret.data.x509; + + if(!(ikey = X509_get_pubkey(issuer))) + { + ErrorLog_push(HR_UNABLE_DEC_ISS_PK, issuer_str); + goto end; + } + + /* verify, if is't correct */ + if(X509_verify(cert,ikey) <= 0) + { + ErrorLog_push(HR_CERT_SIGN_FAIL, issuer_str); + goto end; + } + + EVP_PKEY_free(ikey); + + ikeybitstr = X509_get0_pubkey_bitstr(issuer); + + id = OCSP_cert_id_new(EVP_sha1(), iname, ikeybitstr, X509_get_serialNumber(cert)); + + /* new OCSP request */ + if(!(req = OCSP_REQUEST_new()) || !OCSP_request_add0_id(req, id)) + { + ErrorLog_push(HR_NO_OCSP_REQ, cert_str); + goto end; + } + /* add nonce */ + OCSP_request_add1_nonce(req, NULL, -1); + + /* get Authority access info extension(s) - AAI: + OCSP IP:xx.xx.xx.xx:port + */ + if(!(aAI_str = CRL_OCSP_get_extension_value(ctx, NID_info_access))) + goto end; + + for(index = aAI_str; index; index = index ->next) + { + char *host = NULL, *path = "/", *port = NULL; + int use_ssl = -1; + SSL_CTX *nic = NULL; + BIO *cbio = NULL; + + /* parse AAI */ + if(!CRL_OCSP_parse_extension_value(0 , index->value, &host, &port, &path, &use_ssl)) /* 0 == OCSP */ + { + ErrorLog_push(HR_INV_URL, index->value); + goto for_end; + } + + /* init connection BIO */ + if(!CRL_OCSP_connection_BIO_init(&cbio, &nic, host, port, path, use_ssl)) + goto for_end; + + /* do connect */ + if (BIO_do_connect(cbio) <= 0) + { + ErrorLog_push(HR_CONNECTION_FAILED, "OCSP", host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto for_end; + } + + /* get OCSP response */ + resp = OCSP_sendreq_bio(cbio, path, req); + if(!resp) + { + ErrorLog_push(HR_NO_RES, "OCSP", host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto for_end; + } + + for_end: + BIO_free_all(cbio); + if (use_ssl != -1) + { + OPENSSL_free(host); + OPENSSL_free(port); + OPENSSL_free(path); + SSL_CTX_free(nic); + } + if(resp) + break; + + } + + if (!resp) + goto end; + + /*check OCSP response */ + k = OCSP_response_status(resp); + if (k != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + ErrorLog_push(HR_OCSP_RES_NSF, "status not succesful"); + goto end; + } + bs = OCSP_response_get1_basic(resp); + if(!bs) + { + ErrorLog_push(HR_OCSP_RES_NSF, "unable to parse response"); + goto end; + } + + /*check nonce */ + k = OCSP_check_nonce(req, bs); + if(k <= -1) + { + ErrorLog_push(HR_OCSP_RES_NSF,"no nonce in responce or nonce verification failed"); + goto end; + } + + /* verify signature in OCSP response */ + k = OCSP_basic_verify(bs, NULL, ctx->ctx, 0); + if (k < 0) k = OCSP_basic_verify(bs, NULL, ctx->ctx, 0); + if(k <= 0) + { + ErrorLog_push(HR_OCSP_RES_NSF,"verification failed"); + goto end; + } + + /* get status from OCSP response */ + if(!OCSP_resp_find_status(bs, id, &status, NULL, &rev, &thisupd, &nextupd)) + { + ErrorLog_push(HR_OCSP_RES_NSF,"no status found"); + goto end; + } + + /* check validity of last update and next update */ + if (!OCSP_check_validity(thisupd, nextupd, nsec, nsec)) + { + ErrorLog_push(HR_OCSP_RES_NSF,"invalid time"); + goto end; + } + + switch (status) + { + case V_OCSP_CERTSTATUS_GOOD : /* cert is valid */ + case V_OCSP_CERTSTATUS_REVOKED : /* cert is revoked, but if there is TS check it */ + if(status == V_OCSP_CERTSTATUS_REVOKED && + !TIME_STAMPED_OCSP_status_revoked(rev)) + ErrorLog_push(HR_STATUS, "revoked", cert_str); + else + ok = 1; + break; + default : + ErrorLog_push(HR_STATUS, "unknown", cert_str); + } +end: + OCSP_REQUEST_free(req); + OCSP_RESPONSE_free(resp); + OCSP_BASICRESP_free(bs); + + return ok; +} + +/*--- + Verifies certificate status using CRL and OCSP, and tries to save + CRLs from PKCS7 object. +Args: p7 - pkcs7 object +Result: 1/0 +---*/ + +int CRL_OCSP_status_verify(p7) + PKCS7 *p7; +{ + STACK_OF(X509) *signers = NULL; + int i, ok = 0; + + /*get signers */ + signers = PKCS7_get0_signers(p7, NULL, 0); + + /* for every signer verify status */ + for(i = 0; i < sk_num(signers); i++) + { + X509 *x = NULL, *issuer = NULL; + X509_STORE_CTX *ctx = NULL; + X509_OBJECT ret; + X509_NAME *i_name = NULL; + EVP_PKEY *i_pubkey = NULL; + + ok = 0; + + x = sk_X509_value(signers, i); + i_name = X509_get_issuer_name(x); + + /* create and init verify ctx */ + ctx = X509_STORE_CTX_new(); + if (!X509_STORE_CTX_init(ctx, s_cert_store, x, p7->d.sign->cert)) + goto for_end; + + if(!ctx->cert) { + ErrorLog_push(HR_NO_CERT_IN_VER_C); + goto for_end; + } + + /* verify status against local CRL */ + if(!CRL_status_verify(ctx) && IS_OFF(OCSP_CHECK)) + goto for_end; + + /* verify status querying OCSP responder */ + if(!OCSP_status_verify(ctx)) + goto for_end; + + ok = 1; + for_end: + X509_STORE_CTX_free(ctx); + if(!ok) goto end; + } + + ok = 1; + +end: + sk_X509_free(signers); + + return ok; + +} + +/*--- + ? application/x-timestamp +Args: part - 2nd part +Result: 1/0 +---*/ +int is_timestamp_reply(part) + PART *part; +{ + PART *p = part; + BODY *body; + + if(p && p->next && (body = &p->next->body) && + body->subtype && !strucmp(body->subtype,"x-timestamp")) + return 1; + + return 0; +} + +/*--- + ? multipart/x-timestamp + 2nd part application/x-timestamp +Args: body - +Result: 1/0 +---*/ +int is_timestamped(body) + BODY *body; +{ + return (body->type == TYPEMULTIPART && body->subtype && !strucmp(body->subtype,"x-timestamp") && + is_timestamp_reply(body->nested.part)); +} + +/*--- + Get Time stamp from 2nd part of multipart message +Args: msgno - message number + section - section with TS +Result: if error occured NULL else TS. +---*/ +TS_RESP *get_TS_RESPONSE(int msgno,const char *section) +{ + STORE_S *store = NULL; + TS_RESP *r = NULL; + BIO *in = NULL; + + store = get_part_contents(msgno,section); + + if (store) + { + in = BIO_new_so(store); + if (in) + { + r=d2i_TS_RESP_bio(in,NULL); + } + } + + if (store) + so_give(&store); + + BIO_free(in); + return r; +} + +/*--- + Cleanup function for body->sparep +Args: b - body +Result: - +---*/ + +void body_cleanup_TS_RESPONSE(b) + BODY *b; +{ + if(b->sparep) + { + TS_RESP_free(b->sparep); + b->sparep = NULL; + } +} + +/*--- + Create verify context. +Args: query - + data - BIO with 1st raw part +Result: if error occured NULL else new verify context. +---*/ + +TS_VERIFY_CTX *time_stamp_create_verify_ctx(query, data) + TS_REQ *query; + BIO *data; +{ + int ok = 0; + TS_VERIFY_CTX *ctx = NULL; + if(data) + { + if (!(ctx = TS_VERIFY_CTX_new())) goto end; + ctx->flags = TS_VFY_VERSION | TS_VFY_SIGNER; + ctx->flags |= TS_VFY_DATA; + ctx->data = data; + } + else if(query) + if (!(ctx = TS_REQ_to_TS_VERIFY_CTX(query, NULL))) + goto end; + + ctx->flags |= TS_VFY_SIGNATURE; + ctx->store = s_cert_store; + + ok = 1; +end: + if(!ok) + { + TS_VERIFY_CTX_free(ctx); + ctx = NULL; + } + return ctx; +} + +/*--- + Verify TS signature, digest,..... +Args: response - TS. + data - BIO with 1st raw part. +Result: 1/0 +---*/ + +int do_TS_RESPONSE_verify(response, data) + TS_RESP *response; + BIO *data; +{ + int ok = 0; + TS_VERIFY_CTX *ctx = NULL; + + /* create verify context. */ + if(!(ctx = time_stamp_create_verify_ctx(NULL, data))) + goto end; + + /* just verify */ + if(!TS_RESP_verify_response(ctx, response)) + goto end; + + ok = 1; +end: + if(ctx) + { + ctx->data = NULL; + ctx->store = NULL; + TS_VERIFY_CTX_free(ctx); + } + + return ok; +} + +/*--- + Verify detached Time stamp. Message should be multipart/x-timestamp with + part 1 - something and part 2 TS_RESP object +Args: b - struct body + msgno - message number + section - checked section +Result: 1/0 +---*/ +int do_detached_timestamp_verify(b, msgno, section) + BODY *b; + int msgno; + char *section; +{ + int ok = 0, result; + PART *p = NULL; + char seq[100]; + STORE_S *toVerify = NULL; + BIO *in = NULL; + TS_RESP *response = NULL; + char what_we_did[256]; + TS_TST_INFO *info = NULL; + + openssl_init(); + + /* get 1st raw part*/ + snprintf(seq,sizeof seq, "%s%s1", section, *section ? "." : ""); + if(!(toVerify = get_raw_part(msgno,seq))) + goto end; + + if(!(in = BIO_new_so(toVerify))) + { + ErrorLog_push(HR_UNABLE_CREAT_BIO); + goto end; + } + + /* get TS_RESP object with TS */ + snprintf(seq,sizeof seq, "%s%s2", section, *section ? "." : ""); + if(!(response = get_TS_RESPONSE(msgno, seq))) + { + ErrorLog_push(HR_UNABLE_PRS_TS_RESP); + goto end; + } + + /* verify it all */ + ErrorLog->err_no = MODE_TIME_STAMP; + if(!(result = do_TS_RESPONSE_verify(response, in))) + ErrorLog_push(HR_VERIFICATION_FAILED, openssl_error_string()); + + /* write information about checked message*/ + if (b->subtype) fs_give((void**) &b->subtype); + b->subtype = cpystr("timestamp"); + b->encoding = ENC8BIT; + + + snprintf(what_we_did,sizeof what_we_did, "This message was time stamped.%s", + result ? "" : " Verification failed"); + + if(b->description) fs_give((void**) &b->description); + b->description = cpystr(what_we_did); + + /* save TS for later */ + b->sparep = response; + b->cleanup = body_cleanup_TS_RESPONSE; + + p = b->nested.part; + if(p && p->next) + mail_free_body_part(&p->next); + + /* save TS pointer to variable TS_stack */ + if((info = TS_RESP_get_tst_info(response))) + { + TIME_STAMPED *TS_push = NULL; + + TS_push = malloc(sizeof(TIME_STAMPED *)); + if (TS_push) + { + TS_push->time = TS_TST_INFO_get_time(info); + TS_push->next = TS_stack; + TS_stack = TS_push; + ok = 1; + } + } + +end: + ErrorLog->err_no = MODE_GLOB; + BIO_free(in); + + if(toVerify) + so_give(&toVerify); + + return ok; +} + +/* + 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; + int status = 1; + + BIO_reset(in); + + + + result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0); + + if (result) + { + /* verify certificate's status using CRL and OCSP */ + result = CRL_OCSP_status_verify(p7,s_cert_store); + status = 0; + } + + + if(!result) + { + err = ERR_peek_error_line_data(NULL, NULL, &data, NULL); + + if (status && out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)) + PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY); + + if(status) + { + ErrorLog_push(HR_VERIFICATION_FAILED, openssl_error_string()); + return result; + } + + result = 0; + } + + + if(IS_ON(SAVE_CERTS)) + { + STACK_OF(X509) *signers; + int i; + + signers = PKCS7_get0_signers(p7, NULL, 0); + + + if (signers) + for (i=0;isparep) { + 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]; + STACK_OF(X509) *signers; + X509 *cert; + + char *x = NULL; + char what_we_did[256]; + int ok = 0; + + openssl_init(); + + snprintf(seq,sizeof seq, "%s%s1", section, *section ? "." : ""); + + toVerify = get_raw_part(msgno,seq); + + if (toVerify) { + + in = BIO_new_so(toVerify); + if (!in) + { + ErrorLog_push(HR_UNABLE_CREAT_BIO); + goto end; + } + + snprintf(seq,sizeof seq, "%s%s2", section, *section ? "." : ""); + + p7 = get_pkcs7_from_part(msgno,seq); + + if (!p7) + { + ErrorLog_push(HR_UNABLE_LOAD_PKCS7, (char *) openssl_error_string()); + goto end; + } + + ErrorLog->err_no = MODE_SIGNATURE; + 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); + + /* -> body->description what we did? */ + signers= PKCS7_get0_signers(p7, NULL, 0); + cert = sk_X509_value(signers,0); + snprintf(what_we_did, sizeof what_we_did, "This message was cryptographically signed by %s.%s, ^E = details",x_X509_NAME_common_name(X509_get_subject_name, cert), + result ? " S/MIME signature verified ok" : " Verification failed"); + + b->description = cpystr(what_we_did); + + b->sparep = p7; + p7 = NULL; + b->cleanup = smime_body_cleanup; + + /* this part moved to free or write error */ + 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; + ok = 1; + } + +end: + ErrorLog->err_no = MODE_GLOB; + BIO_free(in); + + PKCS7_free(p7); + + if (toVerify) + so_give(&toVerify); + + return result; +} + +static int find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri) +{ + return (my_certificate && + !X509_NAME_cmp(ri->issuer_and_serial->issuer,my_certificate->cert_info->issuer) && + !ASN1_OCTET_STRING_cmp(ri->issuer_and_serial->serial,my_certificate->cert_info->serialNumber)); /* I guess this line should be updated... */ +/* !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,my_certificate->cert_info->serialNumber)); /* original line... */ +} + +static int find_certificate_matching_pkcs7(PKCS7 *p7) +{ + int i, ok = 0; + + STACK_OF(PKCS7_RECIP_INFO) *recips; + + recips = p7->d.enveloped->recipientinfo; + + for (i=0; isparep) + p7 = (PKCS7*) b->sparep; + else + { + + p7 = get_pkcs7_from_part(msgno,section); + if (p7 == NULL) + { + ErrorLog_push(HR_UNABLE_LOAD_PKCS7, (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; + + ErrorLog->err_no = MODE_SIGNATURE; + + 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); + + /* -> body->description, what we did end what we happened? */ + signers= PKCS7_get0_signers(p7, NULL, 0); + cert = sk_X509_value(signers,0); + snprintf(what_we_did, sizeof what_we_did, "This message was cryptographically signed by <%s>.%s, ^E = details",x_X509_NAME_common_name(X509_get_subject_name, cert), + result ? " S/MIME signature verified ok" : " Verification failed"); + + } + else if (!PKCS7_type_is_enveloped(p7)) + { + ErrorLog_push(HR_PKCS7_NOT_REC); + goto end; + } + else + { + /* It *is* enveloped */ + + sprintf(what_we_did,"This message was encrypted."); + ErrorLog->err_no = MODE_CIPHER; + + if (!my_certificate) + { + char *x = generate_file_path(MODE_PRIVATE, TYPE_CERT, who_am_I()); + + ErrorLog_push(HR_NO_PRIVATE_CERT, x ? x : ""); + goto end; + } + + + if (!find_certificate_matching_pkcs7(p7)) { + ErrorLog_push(HR_NO_MATCH_CERT); + goto end; + } + + load_private_key(); + + if (!my_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, my_key, my_certificate, out, 0)) + { + ErrorLog_push(HR_PKCS7_DECRYPT_ERROR, (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) + ErrorLog_push(HR_UNABLE_PARSE_ENC); + 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); + + ErrorLog->err_no = MODE_GLOB; + 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, del_TS_from_stack = 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; + } + } + + /* ? multipart/x-timestamp + part 1 - time stamp thing + part 2 - time stamp object + */ + if(is_timestamped(b)) + del_TS_from_stack = do_detached_timestamp_verify(b, msgno, section); + + 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); + } + + } + + } + + /* pop poitner to current TS in body->sparep */ + /* bad ? */ + if(del_TS_from_stack && TS_stack) + { + TIME_STAMPED *new_top = TS_stack->next; + + fs_give ((void **) &TS_stack); + TS_stack = new_top; + } + + 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's remember-smime-password option is off than forget private keys */ + if(IS_OFF(REM_SMIME_PSWD)) + forget_private_keys(); + + + Openssl_init &= ~ALL_DONE; + + 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); + +static struct key smime_outgoing_keys[] = + {NULL_MENU, + NULL_MENU, + {"<","Back",{MC_SIGN, 2,{'<',','}},KS_EXITMODE}, + NULL_MENU, + NULL_MENU, + NULL_MENU, + PREVPAGE_MENU, + NEXTPAGE_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, +}; +INST_KEY_MENU(smime_outgoing_keymenu, smime_outgoing_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=maxLen) + break; + + if (i != 0) + *b++ = ':'; + sprintf(b,"%02x",md[i]); + b+=2; + } +} +/*--- + Output to S/MIME info screen issuer and subject information +Args: name - issuer/subject + pc +Result: - +---*/ +static void output_X509_NAME(X509_NAME *name,gf_io_t pc) +{ + int i, n; + const char *lvalue; + char rvalue[256]; + + + for (i= X509_NAME_entry_count(name) - 1; i >= 0; i--) + { + X509_NAME_ENTRY *e; + + e = X509_NAME_get_entry(name,i); + if(!e)continue; + + n = OBJ_obj2nid(e->object); + + lvalue = OBJ_nid2sn(n); + sprintf(rvalue,"%-19s%s",convert_id(lvalue),convert_id(lvalue) ? " : " : ""); + gf_puts(rvalue,pc); + + X509_NAME_get_text_by_OBJ(name, e->object,rvalue,sizeof(rvalue)); + gf_puts(rvalue,pc); + gf_puts(NEWLINE,pc); + } +} + +/* + Output a string in a distinctive style + */ +static void gf_puts_uline(const char *txt,gf_io_t pc) +{ + pc(TAG_EMBED); pc(TAG_BOLDON); + gf_puts((char *)txt,pc); + pc(TAG_EMBED); pc(TAG_BOLDOFF); +} + +/* + 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 = (char *)buf + len - 1; + char *b = buf; + + do { + if (!store->readc(&c,store)) { + *b = 0; + return b!=buf; + } + *b++ = c; + } while (c!='\n' && bttyo->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;ittyo->screen_cols * percent / 100; + start = (ps_global->ttyo->screen_cols - len)/2; + + for (i=0;icert_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); + + { + BIO *mb = BIO_new_so(left); + + i2a_ASN1_INTEGER(mb,cert->cert_info->serialNumber); + BIO_puts(mb,"\n"); + BIO_flush(mb); + gf_puts(NEWLINE,spc); + + gf_puts_uline("Validity",spc); + + gf_puts(NEWLINE,spc); + 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); +} + + +#define VERSION 0 +#define POLICY 1 +#define SERIAL 2 +#define TIMESTAMP 3 +#define HASH 4 +#define MESSAGE_DATA 5 +#define ACCURACY 6 +#define ORDERING 7 +#define NONCE 8 +#define TSA 9 +#define EXTENSIONS 10 + +/*--- + Get informations about TS. +Args: info - TS information struct. + type - up. which one to find out in info. +Result: string with correspond. info, if nothing found or error = "" +---*/ + +char *get_TS_info(info, type) + TS_TST_INFO *info; + int type; +{ + BIO *mem = NULL; + char buf[256]; + ASN1_OBJECT *policy_id = NULL; + int len; + + mem = BIO_new(BIO_s_mem()); + + switch(type) + { + /* version */ + case VERSION : + { + int v = TS_TST_INFO_get_version(info); + BIO_printf(mem,"%ld",v ? v : 0); + } + break; + + /* policy id */ + case POLICY : + TS_OBJ_print_bio(mem, TS_TST_INFO_get_policy_id(info)); + break; + /* serial number */ + case SERIAL : + TS_ASN1_INTEGER_print_bio(mem, TS_TST_INFO_get_serial(info)); + break; + + /* time stamp */ + case TIMESTAMP : + ASN1_GENERALIZEDTIME_print(mem, TS_TST_INFO_get_time(info)); + break; + + /* hash, digest */ + case HASH : + case MESSAGE_DATA : + { + TS_MSG_IMPRINT *a = TS_TST_INFO_get_msg_imprint(info); + + if(type == HASH) + { + X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(a); + int i; + + i = OBJ_obj2nid(alg->algorithm); + BIO_printf(mem,"%s", (i == NID_undef) ? "" : OBJ_nid2ln(i)); + } + else + { + ASN1_OCTET_STRING *msg = TS_MSG_IMPRINT_get_msg(a); + BIO_dump_indent(mem, M_ASN1_STRING_data(msg),M_ASN1_STRING_length(msg), 4); + } + + } + break; + + /* accuracy */ + case ACCURACY : + { + TS_ACCURACY *accuracy; + ASN1_INTEGER *seconds, *millis, *micros; + accuracy = TS_TST_INFO_get_accuracy(info); + + seconds = TS_ACCURACY_get_seconds(accuracy); + millis = TS_ACCURACY_get_millis(accuracy); + micros = TS_ACCURACY_get_micros(accuracy); + + if(!seconds) + BIO_printf(mem,"unspecified"); + else + TS_ASN1_INTEGER_print_bio(mem, seconds); + BIO_printf(mem, " seconds, "); + + if(!millis) + BIO_printf(mem,"unspecified"); + else + TS_ASN1_INTEGER_print_bio(mem, millis); + BIO_printf(mem, " millis, "); + + if(!micros) + BIO_printf(mem,"unspecified"); + else + TS_ASN1_INTEGER_print_bio(mem, micros); + BIO_printf(mem, " micros"); + } + break; + + /* ordering */ + case ORDERING : + BIO_printf(mem, "%s", TS_TST_INFO_get_ordering(info) ? "yes" : "no"); + break; + + /* nonce */ + case NONCE : + { + ASN1_INTEGER *nonce = TS_TST_INFO_get_nonce(info); + + if(!nonce) + BIO_printf(mem, ""); + else + TS_ASN1_INTEGER_print_bio(mem, nonce); + } + break; + + /* TSA info */ + case TSA : + { + STACK_OF(CONF_VALUE) *nval; + GENERAL_NAME *tsa_name = TS_TST_INFO_get_tsa(info); + + if ((nval = i2v_GENERAL_NAME(NULL, tsa_name, NULL))) + X509V3_EXT_val_prn(mem, nval, 0, 0); + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + } + break; + + /* time stamp extensions */ + case EXTENSIONS : + { + int i, critical; + X509_EXTENSION *ex; + ASN1_OBJECT *obj; + STACK_OF(X509_EXTENSION) *extensions = TS_TST_INFO_get_exts(info); + + for(i = 0; i < X509v3_get_ext_count(extensions); i++) + { + ex = X509v3_get_ext(extensions, i); + obj = X509_EXTENSION_get_object(ex); + i2a_ASN1_OBJECT(mem, obj); + critical = X509_EXTENSION_get_critical(ex); + BIO_printf(mem, ": %s\n", critical ? "critical" : ""); + if (!X509V3_EXT_print(mem, ex, 0, 4)) + { + BIO_printf(mem, "%4s", ""); + M_ASN1_OCTET_STRING_print(mem, ex->value); + } + BIO_write(mem, "\n", 1); + } + } + break; + } + + /* read corr. info from BIO to buffer */ + len = BIO_read(mem, buf, sizeof buf); + buf[len > 0 ? len : 0] = 0; + BIO_free(mem); + return strlen(buf) ? cpystr(buf) : " or "; +} + +/*--- + Add information about TS to S/MIME screen. +Args: info - TS info + pc - +Result: - +---*/ + +void output_TS_info(info,pc) + TS_TST_INFO *info; + gf_io_t pc; +{ + int v; + + for(v = 0; v <= 10; v++) + { + gf_puts_uline(v == MESSAGE_DATA ? "Message Data" : + v < MESSAGE_DATA ? + (v == SERIAL ? "Serial": + (v < SERIAL ? + (v == VERSION ? "Version" : "Pocily OID") : + (v == TIMESTAMP ? "Time Stamp" : "Hash"))) : + (v == NONCE ? "Nonce" : + (v < NONCE ? + (v == ACCURACY ? "Accuracy" : "Ordering") : + (v == TSA ? "TSA" : "Extensions"))) + ,pc); + gf_puts(NEWLINE,pc); + gf_puts(get_TS_info(info,v), pc); + if ( v != POLICY && v != MESSAGE_DATA) + gf_puts(NEWLINE,pc); + } +} + +void output_smime_info_error(char *left, char *output, char *right, gf_io_t pc, int offset) +{ + char buf[256]; + snprintf(buf, sizeof buf, "%*s%s%s%s"NEWLINE, offset, "", left, output, right); + + gf_puts(buf,pc); +} + +void format_smime_info_error(char *s, EXTENSION_STACK *stack, gf_io_t pc, int offset, char *left, char *right) +{ + + if(!stack) + return; + + output_smime_info_error(left,s, right, pc, offset); + offset += 2; + + while(stack) + { + char buf[MAXPATH]; + char *_1 = NULL, *_2 = NULL; + int _true = 1; + + snprintf(buf,sizeof buf, "%s", stack->value); + + _1 = buf; + while(_2 = strstr(_1, "\r\n")) + { + _true = (_1 == buf); + + _2[0] = '\0'; + output_smime_info_error( _true ? left : "" , _1, "", pc, + _true ? offset : offset + strlen(left)); + _1 = _2 + 2; + } + output_smime_info_error(_true ? left : "" , _1, right, pc, _true ? offset : offset + strlen(left)); + + stack = stack->next; + } + +} + + +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 (!(body->subtype && !strucmp(body->subtype,"timestamp"))) + { + + 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 authentication",plural(sk_X509_num(signers))); + gf_puts_uline(tmp_20k_buf,pc); + gf_puts(NEWLINE,pc); + print_separator_line(100,'-',pc); + + for (i=0;id.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; + + + gf_puts("The algorithm used to encrypt was ",pc); + + + if (alg) { + char *n = (char *)OBJ_nid2sn( OBJ_obj2nid(alg->algorithm )); + + gf_puts(n ? n : "",pc); + + } else gf_puts("",pc); + + gf_puts("." NEWLINE NEWLINE,pc); + + gf_puts_uline("Certificate for decrypting",pc); + gf_puts(NEWLINE,pc); + print_separator_line(100,'-',pc); + + for (i=0;i= sk_PKCS7_RECIP_INFO_num(ris)) + { + gf_puts("No certificate capable of decrypting could not be found."NEWLINE,pc); + gf_puts(NEWLINE, pc); + + } + } + break; + } + } + } + + /* time stamp case */ + else + { + TS_TST_INFO *tst_info = NULL; + + switch(pass) + { + case 1 : + + gf_puts("This message was time stamped." NEWLINE,pc); + break; + + case 2 : + + sprintf(tmp_20k_buf,"About Time Stamp"); + gf_puts_uline(tmp_20k_buf,pc); + gf_puts(NEWLINE,pc); + print_separator_line(100,'-',pc); + + tst_info = TS_RESP_get_tst_info((TS_RESP *)p7); + + if(!tst_info) + { + gf_puts(NEWLINE "TST Info not included", pc); + gf_puts(NEWLINE,pc); + } + else + output_TS_info(tst_info,pc); + + break; + } + } + } +} + +void view_writec(); + +/* coud == >*/ +int smime_info_init(struct pine *ps, int *msgno, ENVELOPE **e, BODY **b) +{ + *msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap)); + *e = mail_fetch_structure (ps->mail_stream, *msgno, + b,0); + + if (!*e || !*b) + { + q_status_message(SM_ORDER, 0, 3, + "Can't fetch body of message."); + return 0; + } + return 1; +} + +void smime_error_out_output(gf_io_t pc) +{ + /* dodelat offset */ + int offset = 4; + char *left_end = "[ "; + char *right_end = " ]"; + + if(!ErrorLog_NOT_EMPTY) + return; + + + gf_puts_uline(NEWLINE,pc); + output_smime_info_error(left_end,"S/MIME Error(s)",right_end, pc, offset); + gf_puts_uline(NEWLINE,pc); + + offset += 3; + + if(ErrorLog->glob) + format_smime_info_error("Global", ErrorLog->glob, pc, offset, left_end, right_end); + + if(ErrorLog->signature) + format_smime_info_error("Signature", ErrorLog->signature, pc, offset, left_end, right_end); + + if(ErrorLog->cipher) + format_smime_info_error("Cipher", ErrorLog->cipher, pc, offset, left_end, right_end); + + if(ErrorLog->time_stamp) + format_smime_info_error("Time Stamp", ErrorLog->time_stamp, pc, offset, left_end, right_end); + + gf_puts_uline(NEWLINE,pc); +} + +void smime_info_output(gf_io_t pc, BODY *body, int msgno) +{ + gf_puts_uline("Overview",pc); + gf_puts(NEWLINE,pc); + + format_smime_info(1,body,msgno,pc); + + smime_error_out_output(pc); + gf_puts(NEWLINE, pc); + format_smime_info(2,body,msgno, pc); +} + +struct smime_info_screen_index_struct xcoud[] = { + { TYPE_INF, &smime_info_keymenu, smime_info_init, smime_info_output}, + { TYPE_ERR_OUT, &smime_outgoing_keymenu, NULL, smime_error_out_output } +}; + +void smime_info_screen(struct pine *ps, int type) +{ + 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; + struct smime_info_screen_index_struct *which = &xcoud[type]; + + if(!which) + return; + + ps_global->prev_screen = smime_info_screen; + ps_global->next_screen = SCREEN_FUN_NULL; + + if(mn_total_cur(ps_global->msgmap) > 1L){ + q_status_message(SM_ORDER | SM_DING, 0, 3, + "Can only view one message's information at a time."); + return; + } + + if(which->init && !(*which->init)(ps_global, &msgno, &env, &body)) + return; + + what = FirstMenu; + + store = so_get(CharStar, NULL, EDIT_ACCESS); + + while(ps_global->next_screen == SCREEN_FUN_NULL){ + + ClearLine(1); + + so_truncate(store,0); + + view_writec_init(store, &handles, HEADER_ROWS(ps_global), + HEADER_ROWS(ps_global) + + ps_global->ttyo->screen_rows - (HEADER_ROWS(ps_global) + + HEADER_ROWS(ps_global))); + + if(which->output) + (*which->output)((gf_io_t)view_writec, body, msgno); + + view_writec_destroy(); + + + ps_global->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 & TIME STAMP 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 & TIME STAMP INFORMATION"; +/* scrollargs.end_scroll = view_end_scroll; */ + scrollargs.resize_exit = 1; + scrollargs.help.text = NULL; + scrollargs.help.title = "HELP FOR S/MIME & TIME STAMP INFORMATION VIEW"; + scrollargs.keys.menu = which->keymenu; + scrollargs.keys.what = what; + setbitmap(scrollargs.keys.bitmap); + + if(scrolltool(&scrollargs) == MC_RESIZE) + offset = scrollargs.start.loc.offset; + } + + so_give(&store); +} + +/*--- + Test if directory exists. If not , it's created. +Args: dir - string with tested directory +Result: 0/1 +---*/ +int is__dir(dir) + const char *dir; +{ + struct stat stats; + int ok = 0, is; + extern int errno; + + is = stat(dir,&stats); + + if(!is && S_ISDIR(stats.st_mode)) + ok = 1; + else if(is == -1 && (errno & ENOENT) && !mkdir(dir,0755)) + ok = 1; + +end: + return ok; +} + +/*--- + Set S/MIME variables. +Args: - +Result: - +---*/ +void set_smime_vars() +{ + register struct variable *vars = ps_global->vars; + struct passwd *unix_pwd; + + /*set smime-options values*/ + + set_current_val(&vars[V_SMIME_OPTIONS], TRUE, TRUE); + { + char **name = NULL; + int i; + + smime_flags = 0; + + if(VAR_SMIME_OPTIONS) + for(name = VAR_SMIME_OPTIONS; *name; *name++) + { + int value = 0; + + for(i = 0; i < SMIME_OPTIONS; i++) + if(!strucmp(smime_options[i].name, *name)) + { + value |= smime_options[i].value; + break; + } + + if(value && IS_OFF(value)) + smime_flags |= value; + } + } + + /* set Global variable ca-dir, private-dir, public-dir values*/ + if((unix_pwd = getpwuid(getuid()))) + { + char buf[256]; + + sprintf(buf,"%s/.pine-smime",unix_pwd->pw_dir); + + if(is__dir(buf)) + { + char buf2[256]; + + sprintf(buf2,"%s/ca",buf); + if(is__dir(buf2)) + GLO_CAPATH = cpystr(buf2); + + sprintf(buf2,"%s/private",buf); + if(is__dir(buf2)) + GLO_KEY_CERT_PATH = cpystr(buf2); + + sprintf(buf2,"%s/public",buf); + if(is__dir(buf2)) + GLO_PUBLIC = cpystr(buf2); + } + } + set_current_val(&vars[V_CAPATH], TRUE, TRUE); /* ca-dir */ + set_current_val(&vars[V_KEY_CERT_PATH], TRUE, TRUE); /* private-dir */ + set_current_val(&vars[V_PUBLIC], TRUE, TRUE); /* public-dir */ + set_current_val(&vars[V_CERTFILE], TRUE, TRUE); /* certfile */ + set_current_val(&vars[V_TSA], TRUE, TRUE); /* TSA */ +} + +/*--- + It take changed BODY by S/MIME support and turn it back to origin. + Called from mailview.c +Args: body - changed body + stream - current mail stream + raw_msgno - message number +Result: - +---*/ +void back_2_original(BODY **body, MAILSTREAM *stream, unsigned long raw_msgno) +{ + char *message = NULL; + char *body_start = NULL; + STRING s; + BODY *b = NULL; + ENVELOPE *e = NULL; + BODY *h = NULL; + + /* get message*/ + if(!(message = mail_fetch_message(stream,raw_msgno,NULL,0))) + return; + + if(!(body_start = strstr(message, "\r\n\r\n"))) + return; + + body_start += 4; + INIT(&s, mail_string, body_start, strlen(body_start)); + + /* parse message -> b */ + rfc822_parse_msg_full(&e,&b,message, body_start - message - 2, &s, BADHOST, 0, 0); + mail_free_envelope(&e); + + /* change *body */ + h = *body; + h->type = b->type; + if(h->cleanup) + (*h->cleanup)(h); + + if(h->subtype) fs_give((void **) &h->subtype); + if(b->subtype) h->subtype = cpystr(b->subtype); + + h->encoding = b->encoding; + + if(h->description) fs_give((void **) &h->description); + if(b->description) h->description = cpystr(b->description); + + if(h->disposition.type) fs_give ((void **) &h->disposition); + if(h->disposition.parameter) fs_give ((void **) &h->disposition.parameter); + if(b->disposition.type) h->disposition.type = b->disposition.type; + if(b->disposition.parameter) h->disposition.parameter = b->disposition.parameter; + if(h->parameter) mail_free_body_parameter(&h->parameter); + + if(b->parameter) h->parameter = b->parameter; + + if(h->nested.part) mail_free_body_part(&h->nested.part); + if(b->nested.part) h->nested.part = b->nested.part; +} + +/*--- + Create Time stamp digest. Taken from apps/ts.c +Args: input - + digest - + md - + mdvalue - +Result: digest length. +---*/ +static int time_stamp_create_digest(BIO *input, char *digest, const EVP_MD *md, + unsigned char **md_value) +{ + int md_value_len; + + md_value_len = EVP_MD_size(md); + if (input) + { + EVP_MD_CTX md_ctx; + unsigned char buffer[4096]; + int length; + *md_value = OPENSSL_malloc(md_value_len); + if (*md_value == 0) goto err; + EVP_DigestInit(&md_ctx, md); + while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) + { + EVP_DigestUpdate(&md_ctx, buffer, length); + } + EVP_DigestFinal(&md_ctx, *md_value, NULL); + } + else + { + long digest_len; + *md_value = string_to_hex(digest, &digest_len); + if (!*md_value || md_value_len != digest_len) + { + OPENSSL_free(*md_value); + *md_value = NULL; + goto err; + } + } + return md_value_len; +err: + return 0; +} + +/*--- + Create nonce for Time stamp query. taken from apps/ts.c +Args: bits - nonce length +Result: return created nonce +---*/ +static ASN1_INTEGER *time_stamp_create_nonce(int bits) +{ + char buf[20]; + ASN1_INTEGER *nonce = NULL; + int len = (bits - 1) / 8 + 1; + int i; + + if (!RAND_bytes(buf, len)) goto err; + for (i = 0; i < len && !buf[i]; ++i); + if (!(nonce = ASN1_INTEGER_new())) goto err; + OPENSSL_free(nonce->data); + nonce->length = len - i; + if (!(nonce->data = OPENSSL_malloc(nonce->length + 1))) goto err; + memcpy(nonce->data, buf + i, nonce->length); + return nonce; +err: + ASN1_INTEGER_free(nonce); + return NULL; +} + +#define NONCE_LENGTH 64 + +/*--- + Create time stamp query. Taken from apps/ts.c +Args: bio - input BIO +Result: return Time stamp Query +---*/ +static TS_REQ *time_stamp_create_query(bio) + BIO *bio; +{ + int ok = 0; + const EVP_MD *md = NULL; + TS_REQ *ts_req = NULL; + TS_MSG_IMPRINT *msg_imprint = NULL; + X509_ALGOR *algo = NULL; + unsigned char *data = NULL; + int len; + ASN1_INTEGER *nonce_asn1 = NULL; + + if(!bio) goto end; + + /* using sha1 */ + if(!(md = EVP_get_digestbyname("sha1"))) goto end; + + if (!(ts_req = TS_REQ_new())) goto end; + + /*version 1 */ + if (!TS_REQ_set_version(ts_req, 1)) goto end; + + if(!(msg_imprint = TS_MSG_IMPRINT_new())) goto end; + + if (!(algo = X509_ALGOR_new())) goto end; + if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md)))) goto end; + if (!(algo->parameter = ASN1_TYPE_new())) goto end; + algo->parameter->type = V_ASN1_NULL; + if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo)) goto end; + + /* create digest */ + if ((len = time_stamp_create_digest(bio, NULL, md, &data)) == 0) + goto end; + + if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len)) goto end; + + if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint)) goto end; + + /* create nonce */ + if(!(nonce_asn1 = time_stamp_create_nonce(NONCE_LENGTH))) goto end; + if(!TS_REQ_set_nonce(ts_req, nonce_asn1)) goto end; + + /* signers cert in time stamp response required */ + if (!TS_REQ_set_cert_req(ts_req, 1)) goto end; + + ok = 1; +end: + if(!ok) + { + TS_REQ_free(ts_req); + ts_req = NULL; + } + TS_MSG_IMPRINT_free(msg_imprint); + X509_ALGOR_free(algo); + OPENSSL_free(data); + ASN1_INTEGER_free(nonce_asn1); + + return ts_req; +} + +/*--- + Create time stamp. First take Time stamp query and send it to TSA, + gets Time stamp response from TSA. +Args: query - time stamp query +Result: time stamp. +---*/ +TS_RESP *time_stamp_get_response(query) + TS_REQ *query; +{ + int ok = 0; + TS_RESP *response = NULL; + char *url = ps_global->VAR_TSA; + char *host = NULL, *path = "/", *port = NULL; + int use_ssl = -1; + int len; + SSL_CTX *nic = NULL; + BIO *cbio = NULL, *mem = NULL; + char tmpbuf[1024]; + char *p, *q; + static char req_txt[] = +"POST %s HTTP/1.1\r\n" +"Host: %s:%s\r\n" +"Content-Type: application/timestamp-query\r\n" +"Accept: application/timestamp-reply\r\n" +"Pragma: no-cache\r\n" +"Content-Length: %d\r\n" +"\r\n"; + int x_we_cancel = 0; + + if(!NOT_NULL_NOT_0(url)) + { + ErrorLog_push(HR_NO_TSA_URL); + goto end; + } + + /* parse TSA url*/ + if(!OCSP_parse_url(url, &host, &port, &path, &use_ssl)) + { + ErrorLog_push(HR_UNABLE_PARSE_URL, url); + goto end; + } + + if((len = i2d_TS_REQ(query, NULL)) <= 0) + goto end; + + /* init connection BIO*/ + if(!CRL_OCSP_connection_BIO_init(&cbio, &nic, host, port, path, use_ssl)) + goto end; + + /* connect and send time stamp query using HTTP*/ + if (BIO_do_connect(cbio) <= 0) + { + ErrorLog_push(HR_CONNECTION_FAILED, "TSA", host, + port ? ":" : "", port ? port : "", + path ? path : ""); + goto end; + } + + if(BIO_printf(cbio, req_txt, path, host, port, len) < 0) + goto end; + + if(!i2d_TS_REQ_bio(cbio, query)) + goto end; + + x_we_cancel = busy_alarm(1, NULL, NULL, 0); + /* get response and get time from it*/ + if(!(mem = parsed_BIO_response(cbio))) + goto end; + + if(!(response = d2i_TS_RESP_bio(mem, NULL))) + goto end; + + ok = 1; +end: + if(x_we_cancel) + cancel_busy_alarm(-1); + + if(!ok) + { + TS_RESP_free(response); + response = NULL; + + ErrorLog_push(HR_TSA_RESPONSE_FAILED); + } + BIO_free_all(cbio); + BIO_free(mem); + if (use_ssl != -1) + { + OPENSSL_free(host); + OPENSSL_free(port); + OPENSSL_free(path); + SSL_CTX_free(nic); + } + return response; +} + +/*--- + Add Time stamp to outgoing message - create TS query, TS response, + verify TS, create new message body including TS. + +Args: Pbody - struct BODY of outgoing message +Result: 0/1. +---*/ +int time_stamp_outgoing_message(Pbody) + BODY **Pbody; +{ + BODY *body = *Pbody, *newBody = NULL; + int ok = 0, len; + STORE_S *store = NULL; + STORE_S *outs = NULL; + BIO *in = NULL, *out = NULL; + TS_REQ *query = NULL; + TS_RESP *response = NULL; + TS_VERIFY_CTX *verify_ctx = NULL; + PART *p1 = NULL, *p2 = NULL; + + /* OpenSSL init */ + openssl_init(); + + store = body_to_store(body); + + in = BIO_new_so(store); + + BIO_reset(in); + + ErrorLog->err_no = MODE_TIME_STAMP; + + + /*create TS query */ + if(!(query = time_stamp_create_query(in))) + { + ErrorLog_push(HR_UNABLE_CREATE_TSA_QUERY); + goto end; + } + + + /* get and verify TS */ + if(!(response = time_stamp_get_response(query))) + goto end; + + if(!(verify_ctx = time_stamp_create_verify_ctx(query, NULL)) + || !TS_RESP_verify_response(verify_ctx, response)) + { + ErrorLog_push(HR_VERIFICATION_FAILED, "TSA response"); + goto end; + } + + /* check if time stamp includes correct time + +/- MAX_VALIDITY_PERIOD (5min) from local */ +/* + { + time_t t_now, t_tmp; + TS_TST_INFO *info = NULL; + ASN1_GENERALIZEDTIME *r_time = NULL; + + + if(!(info = TS_RESP_get_tst_info(response))) + { + E_TIMES_INVALID; + goto end; + } + + r_time = TS_TST_INFO_get_time(info); + time(&t_now); + t_tmp = t_now + MAX_VALIDITY_PERIOD; + if (X509_cmp_time(r_time, &t_tmp) >= 0) + { + err_no = E_TIMES_INVALID; + goto end; + } + + t_tmp = t_now - MAX_VALIDITY_PERIOD; + if (X509_cmp_time(r_time, &t_tmp) <= 0) + { + err_no = E_TIMES_INVALID; + goto end; + } + } +*/ + /* new body - first part is original body without TS, + second part includes TS. + Create Content-Type: multipart/x-timestamp */ + + outs = so_get(CharStar,NULL,EDIT_ACCESS); + out = BIO_new_so(outs); + + i2d_TS_RESP_bio(out, response); + + BIO_flush(out); + so_seek(outs,0,SEEK_SET); + + newBody = mail_newbody(); + + newBody->type = TYPEMULTIPART; + newBody->subtype = cpystr("x-timestamp"); + newBody->encoding = ENC7BIT; + + p1 = mail_newbody_part(); + p2 = mail_newbody_part(); + + p1->body = *body; + p1->next = p2; + + setup_pkcs7_body_for_signature(&p2->body,"Time Stamped Message","x-timestamp","response.tsq"); + + p2->body.contents.text.data = (char*)outs; + + newBody->nested.part = p1; + + *Pbody = newBody; + + ok = 1; +end: + ErrorLog->err_no = MODE_GLOB; + TS_REQ_free(query); + TS_RESP_free(response); + if(verify_ctx) + { + verify_ctx->store = NULL; + TS_VERIFY_CTX_free(verify_ctx); + } + BIO_free(in); + if (store) + so_give(&store); + + return ok; +} + +#define YEAR 0 +#define MONTH 1 +#define DAY 2 +#define HOUR 3 +#define MINUTE 4 +#define SEC 5 +#define MIL 6 +#define Z_PLUS_MINUS 7 + +/*--- + Check parts of ANS1_TIME and convert it from string to int +Args: data - string with ANS1_TIME + how_many - how many digets take from data + type - Z_PLUS_MINUS or other +Result: -1 if error occured else int of first how_many from data. +---*/ + +int ASN1_TIME_get_part(data, how_many, type) + char *data; + int how_many; + int type; +{ + if(!data || (strlen(data) < how_many) || + (type == Z_PLUS_MINUS && *data != 'Z' && *data != '+' && *data != '-')) + return -1; + + if(type == Z_PLUS_MINUS) + return 1; + else + { + int i = 1; + long suma = 0; + for(how_many--; how_many > -1; how_many--) + { + if(!isdigit(data[how_many])) + return -1; + + suma += (data[how_many]-'0')*i; + i *= 10; + } + return suma; + } +} + +/*--- + Convert values from clock to epoch time +Args: clock - year, month, ..... values + offset - seconds +/- to GMT + time - epoch time +Result: - +---*/ + +void *get_Epoch_time(clock, offset, time) + long *clock; + long offset; + time_t *time; +{ + long to_seconds = 60*60*24; + int i, m, how_many; + int month[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + time_t t =0; + + for(i = YEAR; i <= SEC; i++) + { + if(i == YEAR) + { + m = (clock[YEAR] - 1969) / 4; + how_many = (clock[YEAR] - 1970 - m)*365 + m*366; + } + else if(i == MONTH) + { + m = month[clock[MONTH] - 1]; + how_many = (clock[YEAR] % 4) ? m : m + 1; + } + else if(i == DAY) + how_many = clock[DAY] - 1; + else + how_many = clock[i]; + t += how_many * to_seconds; + to_seconds /= (i < DAY) ? 1 : (i == DAY) ? 24 : 60; + } + *time = t - offset; +} + +/*--- + Convert ASN1_TIME to epoch time if is well formatted. +ASN1_TIME: + (YY)YYMMDDhhmmssZ + (YY)YYMMDDhhmmss+hh'mm' + (YY)YYMMDDhhmmss-hh'mm' + + Z indicates that local time is GMT, + + indicates that local time is later than GMT, and + - indicates that local time is earlier than GMT, + hh' is the absolute value of the offset from GMT in hours, and + mm' is the absolute value of the offset from GMT in minutes + +Args: ctm - ASN1_TIME. + time - new epoch time +Result: 1/0. +---*/ +int ASN1_TIME_2_time_t(ASN1_TIME *ctm, time_t *time) +{ + char buff[24], *p; + long clock[6] = { 0, 0, 0, 0, 0, 0}, rv = 0, offset = 0; + int how_many, i; + + if (ctm->type == V_ASN1_UTCTIME) + if((rv = ASN1_TIME_get_part((char *)ctm->data, 2, YEAR)) < 0) + return 0; + + /* add century if it's missing */ + snprintf(buff,sizeof buff,"%s%s", (ctm->type != V_ASN1_UTCTIME) ? "" : + (rv < 50) ? "20" : "19", (char *)ctm->data); + p = buff; + + /* loop sets checks values and sets years, months, ... in variable clock*/ + for(i=YEAR;i<=Z_PLUS_MINUS;i++) + { + if(i == SEC && ASN1_TIME_get_part(p, 1, Z_PLUS_MINUS) > -1) + continue; + else if(i == MIL) + { + if(*p == '.') + { + p++; + while((*p >= '0') && (*p <= '9')) p++; + } + continue; + } + else + how_many = (i && i != Z_PLUS_MINUS) ? 2 : i ? 1 : 4; + + if((rv = ASN1_TIME_get_part(p, how_many, i)) < 0) + return 0; + + if(i < Z_PLUS_MINUS) + { + clock[i] = rv; + p += how_many; + } + } + + /* to GMT */ + if(*p == '+' || *p == '-') + { + if((offset = ASN1_TIME_get_part(p + 1, 2, HOUR) * 60) < 0 || + (rv = ASN1_TIME_get_part(p + 3, 2, MINUTE)) < 0) + return 0; + + offset += rv; + if(*p == '-') + offset = -offset; + } + + /* ASN1_TIME -> epoch time*/ + get_Epoch_time(clock, offset*60, time); + return 1; +} + +/*--- + Compare 2 ASN1_TIME values +Args: ctm1 - + ctm2 - +Result: 0 if error occured, -1 if ctm1 <= ctm2, 1 if ctm1 > ctm2 +---*/ + +int ASN1_TIME_vs_ASN1_TIME(ctm1, ctm2) + ASN1_TIME *ctm1; + ASN1_TIME *ctm2; +{ + time_t t1 = 0, t2 = 0; + int i; + + if(!ASN1_TIME_2_time_t(ctm1, &t1) || !ASN1_TIME_2_time_t(ctm2, &t2)) + return 0; + + i = (t1 == t2) ? 0 : (t1 < t2) ? -1 : 1; + if(i == 0) + return -1; + else + return i; +} + +void pop_all_ErrorLog() +{ + EXTENSION_STACK_pop_all(&ErrorLog->glob); + EXTENSION_STACK_pop_all(&ErrorLog->time_stamp); + EXTENSION_STACK_pop_all(&ErrorLog->signature); + EXTENSION_STACK_pop_all(&ErrorLog->cipher); + + ErrorLog->err_no = 0; +} + +void free_ErrorLog() +{ + pop_all_ErrorLog(); + fs_give((void **)&ErrorLog); +} + +void init_ErrorLog() +{ + if(!ErrorLog) + { + ErrorLog = (ERROR_LOG *)malloc(sizeof(ERROR_LOG)); + } + + ErrorLog->glob = NULL; + ErrorLog->time_stamp = NULL; + ErrorLog->signature = NULL; + ErrorLog->cipher = NULL; + + ErrorLog->err_no = 0; +} +#endif /* SMIME */ diff -ruN original.pine4.61.sources/pine4.61/pine/smime.errors.h smime.pine.for.Linux/pine4.61/pine/smime.errors.h --- original.pine4.61.sources/pine4.61/pine/smime.errors.h Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/smime.errors.h Fri Jul 16 23:17:46 2004 @@ -0,0 +1,142 @@ +#ifdef SMIME + +#define MODE_GLOB 0 +#define MODE_SIGNATURE 1 +#define MODE_TIME_STAMP 2 +#define MODE_CIPHER 3 + +typedef enum { + NO_CA_STORE = 0 + , NO_PRIVATE_CERT + , NO_CRL_DP + , NO_MATCH_CERT + , INCORRECT_PASS + , UNABLE_READ_KEY + , PKCS7_DECRYPT_ERROR + , UNABLE_PARSE_ENC + , PKCS7_NOT_REC + , UNABLE_LOAD_PKCS7 + , UNABLE_CREAT_BIO + , VERIFICATION_FAILED + , UNABLE_PRS_TS_RESP + , NO_VER_CHAIN + , UNABLE_GET_CRL_FOR + , UNABLE_DEC_ISS_PK + , CERT_SIGN_FAIL + , CRL_SIGN_FAIL + , INVALID_CRL_NBF + , CRL_NOT_VALID_YET + , INVALID_CRL_NAF + , CRL_EXPIRES + , STATUS + , NO_CERT_IN_VER_C + , NO_ISSUER + , NO_OCSP_REQ + , INV_URL + , CONNECTION_FAILED + , NO_RES + , OCSP_RES_NSF + , NO_EXTENSION + , CONNECTION_BIO + , UNABLE_READ_CRL + , NOT_200_OK + , UNABLE_PARSE_DATA + , NO_TSA_URL + , UNABLE_PARSE_URL + , TSA_RESPONSE_FAILED + , UNABLE_CREATE_TSA_QUERY + , CERTFILE + , NO_PUBLIC_CERT +} SMIMErrorIndex; + +struct SMIMError { + int code; + char *human_readable; +}; + +static struct SMIMError SMIMErrors[] = { + {NO_CA_STORE, "Unable load trusted CAs certificates and CRLs"}, + {NO_PRIVATE_CERT, "Unable load private certificate \"%s\""}, + {NO_CRL_DP, "No CRL Distribution Points Extension"}, + {NO_MATCH_CERT, "Couldn't find the certificate matching with your private one"}, + {INCORRECT_PASS, "You inserted inccorect passphrase"}, + {UNABLE_READ_KEY, "Couldn't read key: %s"}, + {PKCS7_DECRYPT_ERROR, "Error decrypting PKCS7: %s"}, + {UNABLE_PARSE_ENC, "Encrypted data couldn't be parsed"}, + {PKCS7_NOT_REC, "PKCS7 object not recognised"}, + {UNABLE_LOAD_PKCS7, "Couldn't load PKCS7 object: %s"}, + {UNABLE_CREAT_BIO, "Unable create OpenSSL BIO object"}, + {VERIFICATION_FAILED, "Verification failed : %s"}, + {UNABLE_PRS_TS_RESP, "Unable parse Time Stamp "}, + {NO_VER_CHAIN, "Unable build verification chain of trusted/Untrusted CA's certs"}, + {UNABLE_GET_CRL_FOR, "Unable get CRL - issuer : %s" }, + {UNABLE_DEC_ISS_PK, "Unable decode Issuers key: %s"}, + {CERT_SIGN_FAIL, "Certificate signarure failure: %s"}, + {CRL_SIGN_FAIL, "CRL signarure failure: %s"}, + {INVALID_CRL_NBF, "Invalid Not Before field: %s" }, + {CRL_NOT_VALID_YET, "CRL not valid yet: %s" }, + {INVALID_CRL_NAF, "Invalid Not After field: %s"}, + {CRL_EXPIRES, "CRL has expired: %s"}, + {STATUS, "Certificate status %s %s" }, + {NO_CERT_IN_VER_C, "No certificate in verification ctx"}, + {NO_ISSUER, "No issuer's certificate : %s"}, + {NO_OCSP_REQ, "Unable create OCSP request for %s"}, + {INV_URL, "URL/URI error : %s"}, + {CONNECTION_FAILED, "%s : connection to \"%s%s%s%s\" failed"}, + {NO_RES, "%s : no response from \"%s%s%s%s\""}, + {OCSP_RES_NSF, "OCSP response - %s"}, + {NO_EXTENSION, "%s : no %s extension in certificate %s"}, + {CONNECTION_BIO, "%s : connection BIO - %s "}, + {UNABLE_READ_CRL, "Unable to read CRL"}, + {NOT_200_OK, "%s : \"%s%s%s%s\" didn't return 200 OK"}, + {UNABLE_PARSE_DATA, "Unable parse data"}, + {NO_TSA_URL, "No TSA URL is set in Setup/Config options"}, + {UNABLE_PARSE_URL, "Unable parse URL (%s)"}, + {TSA_RESPONSE_FAILED, "TSA response failed"}, + {UNABLE_CREATE_TSA_QUERY, "Unable create TSA query"}, + {CERTFILE, "Certfile error (%s) : %s"}, + {NO_PUBLIC_CERT, "No certificate for \"%s\" in \"%s\""} +}; + +#define HR_NO_CA_STORE SMIMErrors[NO_CA_STORE].human_readable +#define HR_NO_PRIVATE_CERT SMIMErrors[NO_PRIVATE_CERT].human_readable +#define HR_NO_CRL_DP SMIMErrors[NO_CRL_DP].human_readable +#define HR_NO_MATCH_CERT SMIMErrors[NO_MATCH_CERT].human_readable +#define HR_INCORRECT_PASS SMIMErrors[INCORRECT_PASS].human_readable +#define HR_UNABLE_READ_KEY SMIMErrors[UNABLE_READ_KEY].human_readable +#define HR_PKCS7_DECRYPT_ERROR SMIMErrors[PKCS7_DECRYPT_ERROR].human_readable +#define HR_UNABLE_PARSE_ENC SMIMErrors[UNABLE_PARSE_ENC].human_readable +#define HR_PKCS7_NOT_REC SMIMErrors[PKCS7_NOT_REC].human_readable +#define HR_UNABLE_LOAD_PKCS7 SMIMErrors[UNABLE_LOAD_PKCS7].human_readable +#define HR_UNABLE_CREAT_BIO SMIMErrors[UNABLE_CREAT_BIO].human_readable +#define HR_VERIFICATION_FAILED SMIMErrors[VERIFICATION_FAILED].human_readable +#define HR_UNABLE_PRS_TS_RESP SMIMErrors[UNABLE_PRS_TS_RESP].human_readable +#define HR_NO_VER_CHAIN SMIMErrors[NO_VER_CHAIN].human_readable +#define HR_UNABLE_GET_CRL_FOR SMIMErrors[UNABLE_GET_CRL_FOR].human_readable +#define HR_UNABLE_DEC_ISS_PK SMIMErrors[UNABLE_DEC_ISS_PK].human_readable +#define HR_CERT_SIGN_FAIL SMIMErrors[CERT_SIGN_FAIL].human_readable +#define HR_CRL_SIGN_FAIL SMIMErrors[CRL_SIGN_FAIL].human_readable +#define HR_INVALID_CRL_NBF SMIMErrors[INVALID_CRL_NBF].human_readable +#define HR_CRL_NOT_VALID_YET SMIMErrors[CRL_NOT_VALID_YET].human_readable +#define HR_INVALID_CRL_NAF SMIMErrors[INVALID_CRL_NAF].human_readable +#define HR_CRL_EXPIRES SMIMErrors[CRL_EXPIRES].human_readable +#define HR_STATUS SMIMErrors[STATUS].human_readable +#define HR_NO_CERT_IN_VER_C SMIMErrors[NO_CERT_IN_VER_C].human_readable +#define HR_NO_ISSUER SMIMErrors[NO_ISSUER].human_readable +#define HR_NO_OCSP_REQ SMIMErrors[NO_OCSP_REQ].human_readable +#define HR_INV_URL SMIMErrors[INV_URL].human_readable +#define HR_CONNECTION_FAILED SMIMErrors[CONNECTION_FAILED].human_readable +#define HR_NO_RES SMIMErrors[NO_RES].human_readable +#define HR_OCSP_RES_NSF SMIMErrors[OCSP_RES_NSF].human_readable +#define HR_NO_EXTENSION SMIMErrors[NO_EXTENSION].human_readable +#define HR_CONNECTION_BIO SMIMErrors[CONNECTION_BIO].human_readable +#define HR_UNABLE_READ_CRL SMIMErrors[UNABLE_READ_CRL].human_readable +#define HR_NOT_200_OK SMIMErrors[NOT_200_OK].human_readable +#define HR_UNABLE_PARSE_DATA SMIMErrors[UNABLE_PARSE_DATA].human_readable +#define HR_NO_TSA_URL SMIMErrors[NO_TSA_URL].human_readable +#define HR_UNABLE_PARSE_URL SMIMErrors[UNABLE_PARSE_URL].human_readable +#define HR_TSA_RESPONSE_FAILED SMIMErrors[TSA_RESPONSE_FAILED].human_readable +#define HR_UNABLE_CREATE_TSA_QUERY SMIMErrors[UNABLE_CREATE_TSA_QUERY].human_readable +#define HR_CERTFILE SMIMErrors[CERTFILE].human_readable +#define HR_NO_PUBLIC_CERT SMIMErrors[NO_PUBLIC_CERT].human_readable +#endif diff -ruN original.pine4.61.sources/pine4.61/pine/smime.h smime.pine.for.Linux/pine4.61/pine/smime.h --- original.pine4.61.sources/pine4.61/pine/smime.h Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/smime.h Fri Jul 16 23:17:46 2004 @@ -0,0 +1,111 @@ +#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, int type); +void back_2_original(BODY **body, MAILSTREAM *stream, unsigned long raw_msgno); +int is_timestamped(BODY *body); + + +extern int g_need_passphrase; + +extern int g_do_encrypt; +extern int g_do_sign; +extern int g_do_timestamp; + +extern int smime_flags; +extern int Openssl_init; + + +#define CRL_CHECK 0x1 +#define CRL_CHECK_ALL 0x2 +#define OCSP_CHECK 0x4 +#define SIGN_DEFAULT_ON 0x8 +#define ENC_DEFAULT_ON 0x10 +#define VERIFY_ON 0x20 +#define REM_SMIME_PSWD 0x40 +#ifdef SSL_CERT_DIRECTORY +#define SSL_CERT_DIR 0x80 +#endif +#define SAVE_CERTS 0x100 +#define CRL_OL 0x200 +#define TS_DEFAULT_ON 0x400 + +struct smime_option { + char *name; + unsigned value; +}; + +static struct smime_option smime_options[] = { + {"verify-on", VERIFY_ON}, + {"crl-check", CRL_CHECK}, + {"crl-check-all", CRL_CHECK_ALL}, + {"crls-from-CAs", CRL_OL}, + {"ocsp-check", OCSP_CHECK}, +#ifdef SSL_CERT_DIRECTORY + {"use-SSL_CERT_DIRECTORY=\""SSL_CERT_DIRECTORY"\"", SSL_CERT_DIR}, +#endif + {"sign-default-on", SIGN_DEFAULT_ON}, + {"encrypt-default-on", ENC_DEFAULT_ON}, + {"time-stamp-default_on", TS_DEFAULT_ON}, + {"remember-smime-passphrase", REM_SMIME_PSWD}, + {"save-certs-and-crls", SAVE_CERTS}, + NULL +}; + + +#define EXTENSION_STACK struct extesion_stack +EXTENSION_STACK { + char *value; + EXTENSION_STACK *next; +}; + +#define ERROR_LOG struct errorlog +ERROR_LOG { + EXTENSION_STACK *glob; + EXTENSION_STACK *time_stamp; + EXTENSION_STACK *signature; + EXTENSION_STACK *cipher; + int err_no; /* mode */ +} extern *ErrorLog; + +typedef enum { + TYPE_INF = 0 + , TYPE_ERR_OUT +} smime_info_screen_index; + +struct smime_info_screen_index_struct { + int type; + struct key_menu *keymenu; + int (*init)(); + void (*output)(); +}; + +#define ErrorLog_NOT_EMPTY \ + (ErrorLog && (ErrorLog->glob || ErrorLog->time_stamp || ErrorLog->signature || ErrorLog->cipher)) + +#ifdef SSL_CERT_DIRECTORY +#define SMIME_OPTIONS 11 +#else +#define SMIME_OPTIONS 10 +#endif + +#define IS_ON(value) (smime_flags & value) +#define IS_OFF(value) !IS_ON(value) +#define NOT_NULL_NOT_0(string) (string && strlen(string) > 0) +#define MAX_VALIDITY_PERIOD (5 * 60) + +#define NOTBEFORE_CERT_NOTAFTER(x) \ + (X509_cmp_time(X509_get_notBefore(x), NULL) < 0 && \ + X509_cmp_time(X509_get_notAfter(x), NULL) > 0) + +#define RELOAD_MY_CERTIFICATE 0x1 +#define RELOAD_MY_KEY 0x2 +#define RELOAD_MY_ALL 0x3 +#define OPENSSL_INITED 0x4 +#define ALL_DONE 0x8 +#endif /* SMIME */ + diff -ruN original.pine4.61.sources/pine4.61/pine/smkeys.c smime.pine.for.Linux/pine4.61/pine/smkeys.c --- original.pine4.61.sources/pine4.61/pine/smkeys.c Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/smkeys.c Fri Jul 16 23:17:46 2004 @@ -0,0 +1,369 @@ +#ifdef SMIME + +#include "headers.h" + +#include +#include +#include +#include +#include +#include + +#include "smkeys.h" +#include "smime.h" +#include "smime.errors.h" + +/* + Load trusted CA's certificates and CRL directories + + Args: - + Result: X509_STORE store or NULL +*/ + +X509_STORE *get_ca_store() +{ + X509_LOOKUP *lookup; + X509_STORE *store; + + store=X509_STORE_new(); + if(!store) + goto end; + + + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); + if (lookup == NULL) + goto end; + + if(NOT_NULL_NOT_0(ps_global->VAR_CAPATH)) + { + if(!X509_LOOKUP_add_dir(lookup,ps_global->VAR_CAPATH,X509_FILETYPE_PEM)) + goto end; + } else + X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_PEM); + +#ifdef SSL_CERT_DIRECTORY + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if(lookup == NULL) + goto xx; + + if(IS_ON(SSL_CERT_DIR) && NOT_NULL_NOT_0(SSL_CERT_DIRECTORY)) + { + if(!X509_LOOKUP_add_dir(lookup,SSL_CERT_DIRECTORY,X509_FILETYPE_PEM)) + goto xx; + } else + X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_PEM); + +xx: +#endif + return store; +end: + X509_STORE_free(store); + return NULL; +} + +char *who_am_I() +{ + ADDRESS *who_am_i = generate_from(); + char buf[MAXPATH]; + + if(!who_am_i) + return NULL; + + snprintf(buf, sizeof buf,"%s@%s",who_am_i->mailbox, who_am_i->host); + + return cpystr(buf); +} + +char *generate_file_path(int mode, int type, char *name) +{ + char *public_or_private = mode == MODE_PUBLIC + ? ps_global->VAR_PUBLIC : ps_global->GLO_KEY_CERT_PATH; + + char buf[MAXPATH]; + + if(!NOT_NULL_NOT_0(public_or_private)) + return NULL; + + if(!name) + return NULL; + + snprintf(buf, sizeof buf,"%s/%s.%s", + public_or_private, name, + type == TYPE_CERT ? "crt" : "key"); + + return cpystr(buf); +} + + +EVP_PKEY *load_key(char *pass) +{ + BIO *in; + EVP_PKEY *key; + char *path_2_key = generate_file_path(MODE_PRIVATE, TYPE_KEY, who_am_I()); + + if(!path_2_key) + return NULL; + + if(!(in = BIO_new_file(path_2_key, "r"))) + return NULL; + + key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass); + BIO_free(in); + return key; +} + +char *get_x509_subject_email(X509 *cert) +{ + int i; + X509_NAME *name = X509_get_subject_name(cert); + X509_NAME_ENTRY *ne; + ASN1_IA5STRING *x_email; + + i = X509_NAME_get_index_by_NID(name, NID_pkcs9_emailAddress, -1); + if(i < 0) + return NULL; + + ne = X509_NAME_get_entry(name, i); + x_email = X509_NAME_ENTRY_get_data(ne); + + if(x_email->type != V_ASN1_IA5STRING || + !x_email->data || !x_email->length) + return NULL; + + return BUF_strdup((char *)x_email->data); +} + +int compare_cert_emails(X509 *cert, char *email) +{ + char *emlst = get_x509_subject_email(cert); + + if(!email || !emlst) + return 0; + + return (!strcmp(email, emlst)); +} + +/* + 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. + + Args: - + Result: none +*/ +void save_cert_for(char *email, X509 *cert) +{ + int ok = 0; + BIO *tmp = NULL; + char *path_2_cert = generate_file_path(MODE_PUBLIC, TYPE_CERT, email); + + if(!path_2_cert) + goto end; + + tmp = BIO_new_file(path_2_cert, "w"); + if (tmp) + { + X509_print(tmp,cert); + PEM_write_bio_X509(tmp, cert); + BIO_free(tmp); + ok = 1; + } +end: + if(!ok) + 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. + + Args: - + Result: - +*/ +X509 *get_cert_for(const char *email, int mode) +{ + X509 *cert = NULL; + BIO *in = NULL; + int ok = 0; + char *path_2_cert = generate_file_path(mode, TYPE_CERT, (char *)email); + + + if(!path_2_cert) + return NULL; + + if(!(in = BIO_new_file(path_2_cert, "r"))) + goto end; + + if(!(cert = PEM_read_bio_X509(in, NULL, NULL, NULL))) + goto end; + + if(!compare_cert_emails(cert, (char *)email)) + goto end; + + /* check notBefore and notAfter fields */ +/* + if(!NOTBEFORE_CERT_NOTAFTER(cert)) + { + sprintf(buf,"%s.old",buf2); + rename(buf2, buf); + goto end; + } +*/ + + ok = 1; +end: + if(!ok) + { + X509_free(cert); + cert = NULL; + } + BIO_free(in); + + return cert; +} + + +X509 *get_my_certificate() +{ + return get_cert_for(who_am_I(), MODE_PRIVATE); +} + +EVP_PKEY *get_my_key() +{ + return load_key(""); +} + +/* + Load certfile from given path + Args: path - + untrusted - pointer to loaded untrusted certs + crls - pointer to loaded clrs + Result: 1/0 +*/ +int load_certfile(path, untrusted, crls) + const char *path; + STACK_OF(X509) **untrusted; + STACK_OF(X509_CRL) **crls; +{ + BIO *certs; + int i, ok = 0; + STACK_OF(X509) *othercerts = NULL; + STACK_OF(X509_CRL) *othercrls = NULL; + STACK_OF(X509_INFO) *allcerts = NULL; + X509_INFO *xi; + + + if(!(certs = BIO_new(BIO_s_file()))) + goto end; + + if (BIO_read_filename(certs,path) <= 0) + goto end; + + othercerts = sk_X509_new_null(); + othercrls = sk_X509_CRL_new_null(); + + if(!othercerts || !othercrls) + goto end; + + /* load PEM documents */ + if(!(allcerts = PEM_X509_INFO_read_bio(certs, NULL, NULL, NULL))) + goto end; + + for(i = 0; i < sk_X509_INFO_num(allcerts); i++) + { + xi = sk_X509_INFO_value (allcerts, i); + + /* cert ? */ + if (xi->x509) + { + /* check validity */ + if(!NOTBEFORE_CERT_NOTAFTER(xi->x509)) + { + /* E_CRL_CERT_TIME; */ + goto end; + } + + /* push it */ + sk_X509_push(othercerts, xi->x509); + xi->x509 = NULL; + } + + /* CRL ? */ + if (xi->crl) + { + /* check validity */ + if (X509_cmp_time(X509_CRL_get_lastUpdate(xi->crl), NULL) >= 0 || + (X509_CRL_get_nextUpdate(xi->crl) && + X509_cmp_time(X509_CRL_get_nextUpdate(xi->crl), NULL) <=0)) + { +/* err_no = E_CRL_CERT_TIME; */ + goto end; + } + + /* push it */ + sk_X509_CRL_push(othercrls, xi->crl); + xi->crl = NULL; + } + } + + ok = 1; +end: + if(!ok) /* something was wrong */ + { + sk_X509_free(othercerts); + sk_X509_CRL_free(othercrls); + othercerts = NULL; + othercrls = NULL; + /* dodelat */ +/* q_status_message2(SM_ORDER,1,1, + "Unable to use certfile <%s> %s",(char *)path, "" ); */ + } + + if (allcerts) + sk_X509_INFO_pop_free(allcerts, X509_INFO_free); + if (certs) + BIO_free(certs); + *untrusted = othercerts; + *crls = othercrls; + return ok; +} + +/* + Save CRL to CA-dir + Args: CRL to be saved + Result: - +*/ +void CRL_save_ol_CRL(crl) + X509_CRL *crl; +{ + char buf[MAXPATH], buf_old[MAXPATH]; + int ok = 0; + BIO *out = NULL; + + if(!NOT_NULL_NOT_0(ps_global->VAR_CAPATH)) + goto end; + + /* hashed cert subject */ + sprintf(buf,"%s/%08lx.r0",ps_global->VAR_CAPATH,X509_NAME_hash(X509_CRL_get_issuer(crl))); + sprintf(buf_old,"%s.old",buf); + + rename(buf,buf_old); + + if(!(out=BIO_new(BIO_s_file()))) + goto end; + + if (BIO_write_filename(out,buf) <= 0) + goto end; + + if(PEM_write_bio_X509_CRL(out,crl)) + { + ok = 1; + goto end; + } +end: + BIO_free_all(out); + if(!ok) + q_status_message1(SM_ORDER,1,1,"Couldn't save CRL %s",buf); + +} +#endif /* SMIME */ diff -ruN original.pine4.61.sources/pine4.61/pine/smkeys.h smime.pine.for.Linux/pine4.61/pine/smkeys.h --- original.pine4.61.sources/pine4.61/pine/smkeys.h Thu Jan 1 01:00:00 1970 +++ smime.pine.for.Linux/pine4.61/pine/smkeys.h Fri Jul 16 23:17:46 2004 @@ -0,0 +1,21 @@ +#ifdef SMIME + +X509_STORE *get_ca_store(); +X509 *get_cert_for(const char *email, int mode); +void save_cert_for(char *email,X509 *cert); +int load_certfile(const char *path, STACK_OF(X509) **untrusted, STACK_OF(X509_CRL) **crls); +void CRL_save_ol_CRL(X509_CRL *crl); +EVP_PKEY *load_key(char *pass); +char *get_x509_subject_email(X509 *x); +X509 *get_my_certificate(); +EVP_PKEY *get_my_key(); +char *who_am_I(); +char *generate_file_path(int mode, int type, char *name); + +#define MODE_PUBLIC 1 +#define MODE_PRIVATE 0 + +#define TYPE_KEY 0 +#define TYPE_CERT 1 + +#endif /* SMIME */