diff -Ncr pine4.58/imap/src/c-client/mail.c pine4.58_smime/imap/src/c-client/mail.c *** pine4.58/imap/src/c-client/mail.c Tue Sep 9 19:18:53 2003 --- pine4.58_smime/imap/src/c-client/mail.c Sat May 8 14:02:17 2004 *************** *** 5346,5351 **** --- 5346,5355 ---- void mail_free_body_data (BODY *body) { + /* cleanup body if requested by application */ + if (body->cleanup) + (*body->cleanup)(body); + switch (body->type) { /* free contents */ case TYPEMULTIPART: /* multiple part */ mail_free_body_part (&body->nested.part); diff -Ncr pine4.58/imap/src/c-client/mail.h pine4.58_smime/imap/src/c-client/mail.h *** pine4.58/imap/src/c-client/mail.h Tue Aug 19 23:46:41 2003 --- pine4.58_smime/imap/src/c-client/mail.h Sat May 8 14:02:17 2004 *************** *** 678,683 **** --- 678,684 ---- } size; char *md5; /* MD5 checksum */ void *sparep; /* spare pointer reserved for main program */ + void (*cleanup)(BODY *); /* cleanup function */ }; diff -Ncr pine4.58/pine/README.smime pine4.58_smime/pine/README.smime *** pine4.58/pine/README.smime Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/README.smime Sat May 8 14:15:27 2004 *************** *** 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 -Ncr pine4.58/pine/TODO.smime pine4.58_smime/pine/TODO.smime *** pine4.58/pine/TODO.smime Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/TODO.smime Sat May 8 14:02:17 2004 *************** *** 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 -Ncr pine4.58/pine/bss_so.c pine4.58_smime/pine/bss_so.c *** pine4.58/pine/bss_so.c Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/bss_so.c Sat May 8 14:02:17 2004 *************** *** 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 -Ncr pine4.58/pine/bss_so.h pine4.58_smime/pine/bss_so.h *** pine4.58/pine/bss_so.h Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/bss_so.h Sat May 8 14:02:17 2004 *************** *** 0 **** --- 1 ---- + BIO *BIO_new_so(STORE_S *so); diff -Ncr pine4.58/pine/filter.c pine4.58_smime/pine/filter.c *** pine4.58/pine/filter.c Mon Apr 7 21:34:40 2003 --- pine4.58_smime/pine/filter.c Sat May 8 14:02:17 2004 *************** *** 903,909 **** /* get a character from a file */ ! /* assumes gf_out struct is filled in */ int gf_freadc(c) unsigned char *c; --- 903,909 ---- /* get a character from a file */ ! /* assumes gf_in struct is filled in */ int gf_freadc(c) unsigned char *c; *************** *** 938,944 **** /* get a character from a string, return nonzero if things OK */ ! /* assumes gf_out struct is filled in */ int gf_sreadc(c) unsigned char *c; --- 938,944 ---- /* get a character from a string, return nonzero if things OK */ ! /* assumes gf_in struct is filled in */ int gf_sreadc(c) unsigned char *c; diff -Ncr pine4.58/pine/init.c pine4.58_smime/pine/init.c *** pine4.58/pine/init.c Fri May 23 18:10:06 2003 --- pine4.58_smime/pine/init.c Sat May 8 14:02:17 2004 *************** *** 67,73 **** #include "headers.h" #include "../c-client/imap4r1.h" /* for LEVELSTATUS() */ - typedef enum {Sapling, Seedling, Seasoned} FeatureLevel; #define TO_BAIL_THRESHOLD 60 --- 67,72 ---- *************** *** 399,404 **** --- 398,416 ---- 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 *************** *** 579,584 **** --- 591,613 ---- cf_text_mimetype_path}, {"url-viewers", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, cf_text_browser}, + + #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. *************** *** 1485,1491 **** GLO_NORM_FORE_COLOR = cpystr(colorx(DEFAULT_NORM_FORE)); GLO_NORM_BACK_COLOR = cpystr(colorx(DEFAULT_NORM_BACK)); #endif - /* * Default first value for addrbook list if none set. * We also want to be sure to set global_val to the default --- 1514,1519 ---- *************** *** 1796,1801 **** --- 1824,1834 ---- set_current_val(&vars[V_ALT_ADDRS], TRUE, TRUE); set_current_val(&vars[V_ABOOK_FORMATS], TRUE, TRUE); + #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]){ init_error(ps, SM_ORDER | SM_DING, 3, 5, diff -Ncr pine4.58/pine/mailcmd.c pine4.58_smime/pine/mailcmd.c *** pine4.58/pine/mailcmd.c Sat Aug 30 00:25:26 2003 --- pine4.58_smime/pine/mailcmd.c Sat May 8 14:02:17 2004 *************** *** 53,58 **** --- 53,61 ---- #include "headers.h" #include "../c-client/imap4r1.h" + #ifdef SMIME + #include "smime.h" + #endif /* * Internal Prototypes *************** *** 1441,1446 **** --- 1444,1462 ---- break; + #ifdef SMIME + /*------- Try to decrypt message -----------*/ + case MC_DECRYPT: + if (g_need_passphrase) + get_passphrase(); + a_changed = TRUE; + break; + + case MC_SECURITY: + state->next_screen = smime_info_screen; + break; + #endif + /*------- Bounce -----------*/ case MC_BOUNCE : cmd_bounce(state, msgmap, 0); diff -Ncr pine4.58/pine/mailview.c pine4.58_smime/pine/mailview.c *** pine4.58/pine/mailview.c Fri Aug 29 23:29:08 2003 --- pine4.58_smime/pine/mailview.c Sat May 8 14:02:17 2004 *************** *** 48,53 **** --- 48,56 ---- #include "headers.h" + #ifdef SMIME + #include "smime.h" + #endif /*---------------------------------------------------------------------- Saved state for scrolling text *************** *** 207,214 **** --- 210,222 ---- HELP_MENU, OTHER_MENU, + #ifdef SMIME + {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}}, + {"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}}, + #else NULL_MENU, NULL_MENU, + #endif RCOMPOSE_MENU, NULL_MENU, NULL_MENU, *************** *** 227,232 **** --- 235,242 ---- #define BOUNCE_KEY 33 #define FLAG_KEY 34 #define VIEW_PIPE_KEY 35 + #define DECRYPT_KEY (VIEW_PIPE_KEY + 3) + #define SECURITY_KEY (DECRYPT_KEY + 1) static struct key simple_text_keys[] = {HELP_MENU, *************** *** 373,379 **** 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; --- 383,388 ---- *************** *** 400,406 **** * 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; --- 409,415 ---- * 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; *************** *** 433,438 **** --- 442,473 ---- 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. *************** *** 460,466 **** ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps) + SCROLL_LINES_BELOW(ps))); ! flags = FM_DISPLAY; if((last_message_viewed != mn_get_cur(ps->msgmap) || last_was_full_header == 1)) flags |= FM_NEW_MESS; --- 495,501 ---- ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps) + SCROLL_LINES_BELOW(ps))); ! flags |= FM_DISPLAY; if((last_message_viewed != mn_get_cur(ps->msgmap) || last_was_full_header == 1)) flags |= FM_NEW_MESS; *************** *** 471,478 **** #ifdef _WINDOWS mswin_noscrollupdate(1); #endif ! (void) format_message(raw_msgno, env, body, &handles, flags, ! view_writec); #ifdef _WINDOWS mswin_noscrollupdate(0); #endif --- 506,513 ---- #ifdef _WINDOWS mswin_noscrollupdate(1); #endif ! (void) format_message(raw_msgno, env, body, &handles, flags, view_writec); ! #ifdef _WINDOWS mswin_noscrollupdate(0); #endif *************** *** 547,552 **** --- 582,596 ---- 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){ /* *************** *** 760,766 **** --- 804,853 ---- } + #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 *************** *** 1700,1705 **** --- 1787,1811 ---- 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++) { *************** *** 6256,6263 **** } - - /*---------------------------------------------------------------------- Format a strings describing one unshown part of a Mime message --- 6362,6367 ---- *************** *** 6307,6312 **** --- 6411,6446 ---- 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 -Ncr pine4.58/pine/makefile.lnx pine4.58_smime/pine/makefile.lnx *** pine4.58/pine/makefile.lnx Tue Sep 10 23:34:30 2002 --- pine4.58_smime/pine/makefile.lnx Sat May 8 14:02:17 2004 *************** *** 60,79 **** LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ mailview.o other.o pine.o strings.o takeaddr.o STDLIBS= -lncurses LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ `cat $(CCLIENTDIR)/LDFLAGS` STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ $(STDCFLAGS) OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \ ! os.o HFILES= headers.h os.h pine.h context.h helptext.h \ $(PICODIR)/headers.h $(PICODIR)/estruct.h \ --- 60,92 ---- LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ mailview.o other.o pine.o strings.o takeaddr.o + SSLDIR= /home/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) \ $(STDCFLAGS) OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \ ! os.o bss_so.o smime.o smkeys.o HFILES= headers.h os.h pine.h context.h helptext.h \ $(PICODIR)/headers.h $(PICODIR)/estruct.h \ *************** *** 135,137 **** --- 148,154 ---- osdep/sendmail osdep/execview \ osdep/postreap.wtp osdep/os-lnx.ic cd osdep; $(MAKE) includer os-lnx.c; cd .. + + jon.o: jon.c + $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -c $< -o $@ + diff -Ncr pine4.58/pine/makefile.so5 pine4.58_smime/pine/makefile.so5 *** pine4.58/pine/makefile.so5 Wed Oct 24 00:24:09 2001 --- pine4.58_smime/pine/makefile.so5 Sat May 8 14:02:17 2004 *************** *** 62,67 **** --- 62,78 ---- LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \ mailview.o other.o pine.o strings.o takeaddr.o + SSLDIR= $(HOME)/local/ssl + SSLCERTS= $(SSLDIR)/certs + SSLINCLUDE= $(SSLDIR)/include + SSLLIB= $(SSLDIR)/lib + + SSLCFLAGS= -I$(SSLINCLUDE) \ + -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \ + -DSMIME + SSLLDFLAGS= -L$(SSLLIB) -lcrypto + + # LDCC= /usr/bin/cc # If you don't have /usr/bin/cc (our Solaris 2.2 doesn't seem to have it, # it only has /usr/ucb/cc) then change LDCC to the following line and *************** *** 72,88 **** STDLIBS= -ltermlib LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ `cat $(CCLIENTDIR)/LDFLAGS` STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ $(STDCFLAGS) OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \ ! os.o HFILES= headers.h os.h pine.h context.h helptext.h \ $(PICODIR)/headers.h $(PICODIR)/estruct.h \ --- 83,101 ---- STDLIBS= -ltermlib LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \ + $(SSLLDFLAGS) \ `cat $(CCLIENTDIR)/LDFLAGS` STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \ + $(SSLCFLAGS) \ $(STDCFLAGS) OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \ ! os.o bss_so.o smime.o smkeys.o HFILES= headers.h os.h pine.h context.h helptext.h \ $(PICODIR)/headers.h $(PICODIR)/estruct.h \ diff -Ncr pine4.58/pine/other.c pine4.58_smime/pine/other.c *** pine4.58/pine/other.c Thu Aug 28 20:06:13 2003 --- pine4.58_smime/pine/other.c Sat May 8 14:02:17 2004 *************** *** 50,55 **** --- 50,59 ---- #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)) *************** *** 1306,1313 **** add_hidden_vars_title = 1; if(exclude_config_var(ps, vtmp, expose_hidden_config)) ! continue; if(add_hidden_vars_title){ add_hidden_vars_title = 0; --- 1310,1326 ---- add_hidden_vars_title = 1; if(exclude_config_var(ps, vtmp, expose_hidden_config)) ! 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; *************** *** 1399,1404 **** --- 1412,1474 ---- 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); } *************** *** 7181,7186 **** --- 7251,7265 ---- 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 : *************** *** 7698,7703 **** --- 7777,7791 ---- 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); *************** *** 8785,8790 **** --- 8873,8943 ---- } } + #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 *************** *** 8807,8813 **** rv = 1; toggle_feature_bit(ps, (*cl)->varmem, (*cl)->var, *cl, 0); } ! else q_status_message(SM_ORDER | SM_DING, 3, 6, "Programmer botch! Unknown checkbox type."); --- 8960,8975 ---- rv = 1; toggle_feature_bit(ps, (*cl)->varmem, (*cl)->var, *cl, 0); } ! 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 -Ncr pine4.58/pine/pine.h pine4.58_smime/pine/pine.h *** pine4.58/pine/pine.h Tue Sep 9 23:52:38 2003 --- pine4.58_smime/pine/pine.h Sat May 8 14:02:17 2004 *************** *** 621,626 **** --- 621,636 ---- , V_MAILCAP_PATH , V_MIMETYPE_PATH , V_BROWSER + #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. *************** *** 846,851 **** --- 856,874 ---- #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 *************** *** 2130,2135 **** --- 2153,2160 ---- } cmdline_val; /* user typed as cmdline arg */ }; + #define MC_DECRYPT 800 + #define MC_SECURITY 801 /* *************** *** 4503,4508 **** --- 4528,4540 ---- 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 -Ncr pine4.58/pine/pine.hlp pine4.58_smime/pine/pine.hlp *** pine4.58/pine/pine.hlp Tue Sep 9 23:19:06 2003 --- pine4.58_smime/pine/pine.hlp Sat May 8 14:02:17 2004 *************** *** 1568,1573 **** --- 1568,1575 ----
  • Reporting Problems
  • Show Available Options +
  • S/MIME patch +
  • Index to Pine's Online Help *************** *** 3036,3041 **** --- 3038,3044 ----
  • Setup Command
  • Signature Editor Commands Explained
  • Simple Text View Screen Explained +
  • S/MIME patch
  • Sort Command
  • Spell Check Command
  • Suspend Command *************** *** 18019,18024 **** --- 18022,18087 ---- <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 ===== *************** *** 26008,26010 **** --- 26071,26080 ---- ========== h_select_by_smaller_size ========== Enter a number or ^C to cancel. All messages less than this many characters in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000). + ========== h_config_sign_default_on ========== + If enabled, the 'Sign' option will default to on when sending messages. + ========== h_config_encrypt_default_on ========== + If enabled, the 'Encrypt' option will default to on when sending messages. + ========== h_config_remember_smime_passphrase ========== + If enabled, you will only have to enter your passphrase for your private key + once during a pine session. diff -Ncr pine4.58/pine/send.c pine4.58_smime/pine/send.c *** pine4.58/pine/send.c Sat Aug 30 00:03:08 2003 --- pine4.58_smime/pine/send.c Sat May 8 14:02:17 2004 *************** *** 50,55 **** --- 50,56 ---- #include "../c-client/smtp.h" #include "../c-client/nntp.h" + #include "smime.h" #ifndef TCPSTREAM #define TCPSTREAM void *************** *** 5737,5742 **** --- 5738,5767 ---- 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; no_help = (i >= 12); *************** *** 5821,5826 **** --- 5846,5898 ---- 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++ = ')'; *************** *** 5922,5927 **** --- 5994,6009 ---- * 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]", *************** *** 6665,6670 **** --- 6747,6753 ---- char *verbose_file = NULL; BODY *bp = NULL; PINEFIELD *pf; + BODY *origBody = body; #define MAX_ADDR_ERROR 2 /* Only display 2 address errors */ *************** *** 6681,6686 **** --- 6764,6811 ---- 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 */ *************** *** 6989,6994 **** --- 7114,7137 ---- 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); *************** *** 9032,9044 **** dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0)); if (body) switch (body->type) { case TYPEMULTIPART: /* multi-part */ ! if (!body->parameter) { /* cookie not set up yet? */ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/ sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0), getpid ()); ! body->parameter = mail_newbody_parameter (); ! body->parameter->attribute = cpystr ("BOUNDARY"); ! body->parameter->value = cpystr (tmp); } part = body->nested.part; /* encode body parts */ do pine_encode_body (&part->body); --- 9175,9191 ---- dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0)); if (body) switch (body->type) { case TYPEMULTIPART: /* multi-part */ ! if (!body->parameter || strucmp(body->parameter->attribute,"BOUNDARY")!=0) { /* cookie not set up yet? */ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/ + PARAMETER *param; + sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0), getpid ()); ! param = mail_newbody_parameter (); ! param->next = body->parameter; ! param->attribute = cpystr ("BOUNDARY"); ! param->value = cpystr (tmp); ! body->parameter = param; } part = body->nested.part; /* encode body parts */ do pine_encode_body (&part->body); diff -Ncr pine4.58/pine/smime.c pine4.58_smime/pine/smime.c *** pine4.58/pine/smime.c Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/smime.c Sat May 8 14:02:17 2004 *************** *** 0 **** --- 1,4209 ---- + /* + 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); + + 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,"L")) + return "Locality"; + else if(!strucmp(lvalue,"ST")) + return "State or Providence"; + 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 "Em@il"; + 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); + } + + + 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]; + + 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? */ + snprintf(what_we_did, sizeof what_we_did, "This message was cryptographically signed.%s", + 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_INTEGER_cmp(ri->issuer_and_serial->serial,my_certificate->cert_info->serialNumber)); + } + + 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? */ + snprintf(what_we_did, sizeof what_we_did, "This message was cryptographically signed.%s", + sigok ? " 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_SECURITY, 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); + + sprintf(buf,"%d",ASN1_INTEGER_get(cert->cert_info->serialNumber)); + gf_puts(buf,spc); + gf_puts(NEWLINE,spc); + gf_puts(NEWLINE,spc); + + gf_puts_uline("Validity",spc); + gf_puts(NEWLINE,spc); + { + BIO *mb = BIO_new_so(left); + + gf_puts("Not Before: ",spc); + ASN1_UTCTIME_print(mb,cert->cert_info->validity->notBefore); + BIO_flush(mb); + gf_puts(NEWLINE,spc); + + gf_puts("Not After: ",spc); + ASN1_UTCTIME_print(mb,cert->cert_info->validity->notAfter); + BIO_flush(mb); + + gf_puts(NEWLINE,spc); + gf_puts(NEWLINE,spc); + + BIO_free(mb); + } + + } + + gf_clear_so_writec(left); + + gf_set_so_writec(&spc,right); + + if (!cert->cert_info) { + gf_puts("Couldn't find certificate info.",spc); + gf_puts(NEWLINE,spc); + } else { + gf_puts_uline("Issuer",spc); + gf_puts(NEWLINE, spc); + + output_X509_NAME(cert->cert_info->issuer,spc); + gf_puts(NEWLINE,spc); + } + + gf_clear_so_writec(right); + + side_by_side(left,right,pc); + + gf_puts_uline("SHA1 Fingerprint",pc); + gf_puts(NEWLINE,pc); + get_fingerprint(cert,EVP_sha1(),buf,sizeof(buf)); + gf_puts(buf,pc); + gf_puts(NEWLINE,pc); + + gf_puts_uline("MD5 Fingerprint",pc); + gf_puts(NEWLINE,pc); + get_fingerprint(cert,EVP_md5(),buf,sizeof(buf)); + gf_puts(buf,pc); + gf_puts(NEWLINE,pc); + + so_give(&left); + so_give(&right); + } + + + #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 signing",plural(sk_X509_num(signers))); + gf_puts_uline(tmp_20k_buf,pc); + gf_puts(NEWLINE,pc); + print_separator_line(100,'-',pc); + + for (i=0;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 -Ncr pine4.58/pine/smime.errors.h pine4.58_smime/pine/smime.errors.h *** pine4.58/pine/smime.errors.h Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/smime.errors.h Sat May 8 14:02:17 2004 *************** *** 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 -Ncr pine4.58/pine/smime.h pine4.58_smime/pine/smime.h *** pine4.58/pine/smime.h Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/smime.h Sat May 8 14:02:17 2004 *************** *** 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 -Ncr pine4.58/pine/smkeys.c pine4.58_smime/pine/smkeys.c *** pine4.58/pine/smkeys.c Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/smkeys.c Sat May 8 14:02:17 2004 *************** *** 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 -Ncr pine4.58/pine/smkeys.h pine4.58_smime/pine/smkeys.h *** pine4.58/pine/smkeys.h Thu Jan 1 01:00:00 1970 --- pine4.58_smime/pine/smkeys.h Sat May 8 14:02:17 2004 *************** *** 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 */