diff options
author | Eduardo Chappa <chappa@washington.edu> | 2014-05-31 21:37:10 -0600 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2014-05-31 21:37:10 -0600 |
commit | 60b67de2ba4be4d2bfeeeea685869e9a5a7363c0 (patch) | |
tree | 20831a8341dfe86c0f4c247df9ac5b5ec61b41db | |
parent | d8f387ef722cfb9e694d25c1ab182d01501f1b9d (diff) | |
download | alpine-60b67de2ba4be4d2bfeeeea685869e9a5a7363c0.tar.xz |
* new version 2.19.9991
* S/MIME Alpine would compute incorrectly the signature of a message
that contains 8bit if the option "Enable 8bit ESMTP Negotiation" is
enabled, the message contains 8bit characters and the smtp server
supports 8bit sending.
* Crash while redrawing S/MIME configuration screen when importing a
certificate
* When forwarding a message before opening it, the message might not be
found. The problem is in the forward_body function, where the section
of the body is not correctly set in all instances.
* When forwarding a signed message Alpine might forward the message as
a multipart message, instead of just selecting the body of the
message. Change to forward the signed part only. This aligns Alpine
with what it does when it replies to a similar message.
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | alpine/confscroll.c | 2 | ||||
-rw-r--r-- | alpine/osdep/mswin.rc | 8 | ||||
-rw-r--r-- | alpine/smime.c | 77 | ||||
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | doc/alpine.1 | 2 | ||||
-rw-r--r-- | doc/tech-notes/index.html | 2 | ||||
-rw-r--r-- | doc/tech-notes/tech-notes.txt | 2 | ||||
-rw-r--r-- | mapi/pmapi.c | 2 | ||||
-rw-r--r-- | mapi/pmapi.rc | 8 | ||||
-rw-r--r-- | pith/pine.hlp | 9 | ||||
-rw-r--r-- | pith/reply.c | 23 | ||||
-rw-r--r-- | pith/send.c | 31 | ||||
-rw-r--r-- | pith/smime.c | 216 | ||||
-rw-r--r-- | pith/smime.h | 3 | ||||
-rw-r--r-- | pith/smkeys.c | 25 | ||||
-rw-r--r-- | pith/smkeys.h | 3 |
17 files changed, 340 insertions, 95 deletions
@@ -1 +1 @@ -2.19.999 +2.19.9991 diff --git a/alpine/confscroll.c b/alpine/confscroll.c index 71648202..8d1940a9 100644 --- a/alpine/confscroll.c +++ b/alpine/confscroll.c @@ -3042,6 +3042,8 @@ update_option_screen(struct pine *ps, OPT_SCREEN_S *screen, Pos *cursor_pos) if(screen == NULL || BODY_LINES(ps) < 1) return; + opt_screen = screen; + if(cursor_pos){ cursor_pos->col = 0; cursor_pos->row = -1; /* to tell us if we've set it yet */ diff --git a/alpine/osdep/mswin.rc b/alpine/osdep/mswin.rc index a6ae8c6c..370044fe 100644 --- a/alpine/osdep/mswin.rc +++ b/alpine/osdep/mswin.rc @@ -244,8 +244,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,19,999,0 - PRODUCTVERSION 2,19,999,0 + FILEVERSION 2,19,9991,0 + PRODUCTVERSION 2,19,9991,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -267,12 +267,12 @@ BEGIN #else VALUE "FileDescription", "Alpine\0" #endif - VALUE "FileVersion", "2.19.999\0" + VALUE "FileVersion", "2.19.9991\0" VALUE "InternalName", "alpine\0" VALUE "LegalCopyright", "Copyright 2006-2009 University of Washington, Copyright 2013-2014\0" VALUE "OriginalFilename", "alpine.exe\0" VALUE "ProductName", "alpine\0" - VALUE "ProductVersion", "2.19.999\0" + VALUE "ProductVersion", "2.19.9991\0" END END BLOCK "VarFileInfo" diff --git a/alpine/smime.c b/alpine/smime.c index 1249ec72..caf0581a 100644 --- a/alpine/smime.c +++ b/alpine/smime.c @@ -332,7 +332,8 @@ output_cert_info(X509 *cert, gf_io_t pc) char buf[256]; STORE_S *left,*right; gf_io_t spc; - int len; + int len, error; + STACK_OF(X509) *chain; left = so_get(CharStar, NULL, EDIT_ACCESS); right = so_get(CharStar, NULL, EDIT_ACCESS); @@ -435,16 +436,84 @@ output_cert_info(X509 *cert, gf_io_t pc) gf_puts_uline("SHA1 Fingerprint", pc); gf_puts(NEWLINE, pc); - get_fingerprint(cert, EVP_sha1(), buf, sizeof(buf)); + 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)); + get_fingerprint(cert, EVP_md5(), buf, sizeof(buf), ":"); gf_puts(buf, pc); gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + + gf_puts_uline("Certificate Chain Information", pc); + gf_puts(NEWLINE, pc); + if((chain = get_chain_for_cert(cert, &error, &len)) != NULL){ + X509 *x; + X509_NAME_ENTRY *e; + int i, offset = 2; + char space[256]; + + for(i = 0; i < offset; i++) space[i] = ' '; + + for(i = -1; i < sk_X509_num(chain); i++){ + char buf[256]; + + x = i == -1 ? cert : sk_X509_value(chain, i); + + if(x && x->cert_info){ + if(i>=0){ + space[offset + i + 0] = ' '; + space[offset + i + 1] = '\\'; + space[offset + i + 2] = '-'; + space[offset + i + 3] = ' '; + space[offset + i + 4] = '\0'; + gf_puts(space, pc); + } + else{ + space[offset] = '\0'; + gf_puts(space, pc); + } + if(i >= 0) + gf_puts_uline("Signed by: ", pc); + else + gf_puts_uline("Issued to: ", pc); + + e = X509_NAME_get_entry(x->cert_info->subject, + X509_NAME_entry_count(x->cert_info->subject)-1); + + if(e){ + X509_NAME_get_text_by_OBJ(x->cert_info->subject, e->object, buf, sizeof(buf)); + gf_puts(buf, pc); + gf_puts(NEWLINE, pc); + } + } + else{ + gf_puts("No certificate info found", pc); + gf_puts(NEWLINE, pc); + break; + } + } + e = X509_NAME_get_entry(x->cert_info->issuer, + X509_NAME_entry_count(x->cert_info->issuer)-1); + if(e){ + X509_NAME_get_text_by_OBJ(x->cert_info->issuer, e->object, buf, sizeof(buf)); + space[offset + i + 0] = ' '; + space[offset + i + 1] = '\\'; + space[offset + i + 2] = '-'; + space[offset + i + 3] = ' '; + space[offset + i + 4] = '\0'; + gf_puts(space, pc); + gf_puts_uline("Signed by: ", pc); + gf_puts(buf, pc); + gf_puts(NEWLINE, pc); + } + sk_X509_pop_free(chain, X509_free); + } + gf_puts(NEWLINE, pc); + so_give(&left); so_give(&right); } @@ -939,6 +1008,8 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line) (*ctmp)->help = h_config_smime_certificate_authorities; (*ctmp)->value = cpystr(_("Manage Certificate Authorities")); (*ctmp)->varmem = 11; + + (*ctmp)->next = NULL; } void display_certificate_information(struct pine *ps, X509 *cert, char *email, WhichCerts ctype, int num) @@ -1,7 +1,7 @@ #! /bin/sh # From configure.ac Rev:14 by chappa@washington.edu. # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for alpine 2.19.999. +# Generated by GNU Autoconf 2.69 for alpine 2.19.9991. # # Report bugs to <chappa@washington.edu>. # @@ -730,8 +730,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='alpine' PACKAGE_TARNAME='alpine' -PACKAGE_VERSION='2.19.999' -PACKAGE_STRING='alpine 2.19.999' +PACKAGE_VERSION='2.19.9991' +PACKAGE_STRING='alpine 2.19.9991' PACKAGE_BUGREPORT='chappa@washington.edu' PACKAGE_URL='' @@ -1596,7 +1596,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures alpine 2.19.999 to adapt to many kinds of systems. +\`configure' configures alpine 2.19.9991 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1666,7 +1666,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of alpine 2.19.999:";; + short | recursive ) echo "Configuration of alpine 2.19.9991:";; esac cat <<\_ACEOF @@ -1953,7 +1953,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -alpine configure 2.19.999 +alpine configure 2.19.9991 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2559,7 +2559,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by alpine $as_me 2.19.999, which was +It was created by alpine $as_me 2.19.9991, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3380,7 +3380,7 @@ fi # Define the identity of the package. PACKAGE='alpine' - VERSION='2.19.999' + VERSION='2.19.9991' cat >>confdefs.h <<_ACEOF @@ -20355,7 +20355,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by alpine $as_me 2.19.999, which was +This file was extended by alpine $as_me 2.19.9991, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20421,7 +20421,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -alpine config.status 2.19.999 +alpine config.status 2.19.9991 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/doc/alpine.1 b/doc/alpine.1 index 51e8a70a..a0e78957 100644 --- a/doc/alpine.1 +++ b/doc/alpine.1 @@ -1,4 +1,4 @@ -.TH alpine 1 "Version 2.19.999" +.TH alpine 1 "Version 2.19.9991" .SH NAME alpine \- an Alternatively Licensed Program for Internet News and Email .SH SYNTAX diff --git a/doc/tech-notes/index.html b/doc/tech-notes/index.html index 5a05c1e4..3ec86306 100644 --- a/doc/tech-notes/index.html +++ b/doc/tech-notes/index.html @@ -3,7 +3,7 @@ <BODY> <H1>Alpine Technical Notes</H1> -Version 2.19.999, May 2014 +Version 2.19.9991, May 2014 <H2><A NAME="TOC">Table of Contents</A></H2><P> diff --git a/doc/tech-notes/tech-notes.txt b/doc/tech-notes/tech-notes.txt index e9bed323..356e49c5 100644 --- a/doc/tech-notes/tech-notes.txt +++ b/doc/tech-notes/tech-notes.txt @@ -1,7 +1,7 @@ Alpine Technical Notes - Version 2.19.999, May 2014 + Version 2.19.9991, May 2014 Table of Contents diff --git a/mapi/pmapi.c b/mapi/pmapi.c index 4d4b3101..036e19ef 100644 --- a/mapi/pmapi.c +++ b/mapi/pmapi.c @@ -1952,7 +1952,7 @@ BOOL APIENTRY DllMain( now = time((time_t *)0); tm_now = localtime(&now); - fprintf(ms_global->dfd, "pmapi32.dll for Alpine Version 2.19.999\r\n"); + fprintf(ms_global->dfd, "pmapi32.dll for Alpine Version 2.19.9991\r\n"); fprintf(ms_global->dfd, " Build date: %s\r\n", datestamp); fprintf(ms_global->dfd, " please report all bugs to chappa@gmx.com\r\n"); diff --git a/mapi/pmapi.rc b/mapi/pmapi.rc index ed2e7b7e..815a7a4e 100644 --- a/mapi/pmapi.rc +++ b/mapi/pmapi.rc @@ -98,8 +98,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,19,999,0 - PRODUCTVERSION 2,19,999,0 + FILEVERSION 2,19,9991,0 + PRODUCTVERSION 2,19,9991,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x29L @@ -117,14 +117,14 @@ BEGIN VALUE "Comments", "alpine info: http://patches.freeiz.com/alpine\0" VALUE "CompanyName", "Patches for Alpine\0" VALUE "FileDescription", "Simple MAPI DLL for Alpine for Windows\0" - VALUE "FileVersion", "2.19.999\0" + VALUE "FileVersion", "2.19.9991\0" VALUE "InternalName", "pmapi32\0" VALUE "LegalCopyright", "Copyright ? University of Washington 2006-2009, Eduardo Chappa 2013-2014\0" VALUE "LegalTrademarks", "Apache License, Version 2.0\0" VALUE "OriginalFilename", "pmapi32.dll\0" VALUE "PrivateBuild", " \0" VALUE "ProductName", "Simple MAPI for Alpine for Windows\0" - VALUE "ProductVersion", "2.19.999\0" + VALUE "ProductVersion", "2.19.9991\0" VALUE "SpecialBuild", " \0" END END diff --git a/pith/pine.hlp b/pith/pine.hlp index 91762910..90be0132 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 63 2014-05-17 18:18:38 +Alpine Commit 64 2014-05-31 21:36:57 ============= h_news ================= <HTML> <HEAD> @@ -202,6 +202,9 @@ Additions include: container with default names PublicContainer, PrivateContainer and CAContainer, as appropriate for these files, unless the user has provided some other names. + <LI> S/MIME: Forwarding a message will include the signed part as part + of the text and not as a multipart message, just as the reply + command does. <LI> Add support to selective expunge through a subcommand of the select-apply commands. Read more in the <A HREF="h_index_cmd_expunge">help</A> for the expunge command. @@ -283,6 +286,10 @@ Bugs that have been addressed include: <LI> S/MIME: Forwarding a signed message might make the body contain mime information that is not part of the body, and hence making the body of the message seem wrong. + <LI> S/MIME Alpine would compute incorrectly the signature of a message + that contains 8bit if the option "Enable 8bit ESMTP Negotiation" is + enabled, the message contains 8bit characters and the smtp server + supports 8bit sending. <LI> Crash when tcp connection to NNTP server was lost after connection had been established, but lost immediately afterwards. <LI> Crash with message "lock when already locked", when painting diff --git a/pith/reply.c b/pith/reply.c index 60a7c489..2a8ab158 100644 --- a/pith/reply.c +++ b/pith/reply.c @@ -4,8 +4,8 @@ static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washingto /* * ======================================================================== - * Copyright 2006-2008 University of Washington * Copyright 2013-2014 Eduardo Chappa + * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,9 @@ static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washingto #include "../pith/ablookup.h" #include "../pith/mailcmd.h" #include "../pith/margin.h" +#ifdef SMIME +#include "../pith/smime.h" +#endif /* SMIME */ /* @@ -2249,11 +2252,23 @@ forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_bod } } else if(orig_body->type == TYPEMULTIPART) { - if(orig_body->subtype && !strucmp(orig_body->subtype, "signed") - && orig_body->nested.part){ + if(orig_body->subtype + && ((!strucmp(orig_body->subtype, "signed") && orig_body->nested.part) +#ifdef SMIME + || (!strucmp(orig_body->subtype, "mixed") + && orig_body->nested.part + && orig_body->nested.part->body.type == TYPEMULTIPART + && orig_body->nested.part->body.subtype + && (!strucmp(orig_body->nested.part->body.subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE) + || !strucmp(orig_body->nested.part->body.subtype, "signed"))) + || !strucmp(orig_body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)) +#endif /* SMIME */ + ){ /* only operate on the signed data (not the signature) */ body = forward_body(stream, env, &orig_body->nested.part->body, - msgno, section, msgtext, flags); + msgno, + orig_body->nested.part->body.type == TYPEMULTIPART + ? section : sect_prefix, msgtext, flags); } /*---- Message is multipart ----*/ else if(!(orig_body->subtype && !strucmp(orig_body->subtype, diff --git a/pith/send.c b/pith/send.c index 191e186d..b01ff7e5 100644 --- a/pith/send.c +++ b/pith/send.c @@ -1762,21 +1762,23 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve #ifdef SMIME if(ps_global->smime && (ps_global->smime->do_encrypt || ps_global->smime->do_sign)){ int result; - + STORE_S *so = lmc.so; lmc.so = NULL; - + result = 1; - - if(ps_global->smime->do_sign) - result = sign_outgoing_message(header, &body, 0); - + + if(ps_global->smime->do_sign){ + bp = F_ON(F_ENABLE_8BIT, ps_global) ? first_text_8bit(body) : NULL; + result = sign_outgoing_message(header, &body, 0, &bp); + } + /* need to free new body from encrypt if sign fails? */ if(result && ps_global->smime->do_encrypt) result = encrypt_outgoing_message(header, &body); - + lmc.so = so; - + if(!result) return 0; } @@ -1999,6 +2001,19 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve body_encodings[added_encoding] = body_encodings[ENC8BIT]; save_encoding = bp->encoding; bp->encoding = added_encoding; +#ifdef SMIME + if(ps_global->smime && ps_global->smime->do_sign + && body->nested.part->next + && body->nested.part->next->body.contents.text.data + && body->nested.part->next->body.mime.text.data){ + STORE_S *so; + + so = (STORE_S *) body->nested.part->next->body.contents.text.data; + so_give(&so); + body->nested.part->next->body.contents.text.data = body->nested.part->next->body.mime.text.data; + body->nested.part->next->body.mime.text.data = NULL; + } +#endif /* SMIME */ } } diff --git a/pith/smime.c b/pith/smime.c index 5a86f318..44cc1955 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -77,8 +77,10 @@ CertList * certlist_from_personal_certs(PERSONAL_CERT *pc); #ifdef PASSFILE void load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert); #endif /* PASSFILE */ -STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error); EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt); +void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen); +void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen); +int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag); int (*pith_opt_smime_get_passphrase)(void); int (*pith_smime_import_certificate)(char *, char *, size_t); @@ -2421,8 +2423,8 @@ int same_cert(X509 *x, X509 *cert) char bufcert[256], bufx[256]; int rv = 0; - get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert)); - get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx)); + get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":"); + get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":"); if(strcmp(bufx, bufcert) == 0) rv = 1; @@ -2644,6 +2646,85 @@ free_smime_body_sparep(void **sparep) should be safe to do so. */ +typedef struct smime_filter_s { + void (*filter)(); +} SMIME_FILTER_S; + +SMIME_FILTER_S sig_filter[] = { + {smime_remove_trailing_crlf}, + {smime_remove_folding_space} +}; + +#define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0])) +#define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */ + +void +smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, + char **bodytext, unsigned long *bodylen) +{ + if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2)) + *bodylen -= 2; +} + +void +smime_remove_folding_space(char **mimetext, unsigned long *mimelen, + char **bodytext, unsigned long *bodylen) +{ + char *s = NULL, *t; + unsigned long mlen = *mimelen; + + if(*mimetext){ + for (s = t = *mimetext; t - *mimetext < *mimelen; ){ + if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){ + *s++ = ' '; + t += 3; + mlen -= 2; + } + else + *s++ = *t++; + } + *mimelen = mlen; + } +} + +int +smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag) +{ + int result, i, j, flag; + char *mtext, *btext; + unsigned long mlen, blen; + BIO *in; + + mtext = mimelen ? fs_get(mimelen+1) : NULL; + btext = fs_get(bodylen+1); + + flag = 1; /* silence all failures */ + for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){ + if((in = BIO_new(BIO_s_mem())) == NULL) + return -1; + + (void) BIO_reset(in); + + if(i+1 == TOTAL_SIGFLTR) + flag = nflag; + + if(mimelen) + strncpy(mtext, mimetext, mlen = mimelen); + strncpy(btext, bodytext, blen = bodylen); + for(j = 0; j < TOTAL_FILTERS; j++) + if((i >> j) & 1) + (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen); + if(mtext != NULL) + BIO_write(in, mtext, mlen); + BIO_write(in, btext, blen); + result = do_signature_verify(p7, in, NULL, flag); + BIO_free(in); + } + if(mtext) fs_give((void **)&mtext); + if(btext) fs_give((void **)&btext); + return result; +} + /* * Given a multipart body of type multipart/signed, attempt to verify it. * Returns non-zero if the body was changed. @@ -2699,36 +2780,20 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) BIO_write(in, mimetext, mimelen); BIO_write(in, bodytext, bodylen); - /* Try compatibility with the past and check if this message - * validates when we remove the last two characters. Silence - * any failures first. - */ - flag = (bodylen <= 2 || strncmp(bodytext+bodylen-2, "\r\n", 2)) - ? 0 : 1; - if(((result = do_signature_verify(p7, in, NULL, flag)) == 0) - && bodylen > 2 - && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){ - BUF_MEM *biobuf = NULL; - - BIO_get_mem_ptr(in, &biobuf); - if(biobuf) - BUF_MEM_grow(biobuf, mimelen + bodylen - 2); - - /* test one more time in case this is a remote connection and the - * server massages the message to the point that it sends bogus - * information. In this case, we fetch the message and we process - * it by hand. - */ - flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox)) + if((result = do_signature_verify(p7, in, NULL, 1)) == 0){ + flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox)) ? 0 : 1; - if((result = do_signature_verify(p7, in, NULL, flag)) == 0 + result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag); + if(result < 0) + return modified_the_body; + if(result == 0 && mimelen > 0 /* do not do this for encrypted messages */ && IS_REMOTE(ps_global->mail_stream->mailbox)){ char *fetch; unsigned long hlen, tlen; STORE_S *msg_so; - BIO_free(in); + BIO_free(in); if((in = BIO_new(BIO_s_mem())) != NULL && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL, NULL, &hlen, FT_PEEK)) != NULL @@ -2773,15 +2838,10 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) BIO_write(in, bodytext, bodylen); so_give(&msg_so); - if(((result = do_signature_verify(p7, in, NULL, 1)) == 0) - && bodylen > 2 - && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){ - BUF_MEM *biobuf = NULL; - - BIO_get_mem_ptr(in, &biobuf); - if(biobuf) - BUF_MEM_grow(biobuf, mimelen + bodylen - 2); - result = do_signature_verify(p7, in, NULL, 0); + if((result = do_signature_verify(p7, in, NULL, 1)) == 0){ + result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0); + if(result < 0) + return modified_the_body; } } } @@ -3248,16 +3308,16 @@ gf_puts_uline(char *txt, gf_io_t pc) pc(TAG_EMBED); pc(TAG_BOLDOFF); } - +/* get_chain_for_cert: error and level are mandatory arguments */ STACK_OF(X509) * -get_chain_for_cert(X509 *cert, int *error) +get_chain_for_cert(X509 *cert, int *error, int *level) { STACK_OF(X509) *chain = NULL; X509_STORE_CTX *ctx; X509 *x, *xtmp; int rc; /* return code */ - int level = -1; - + + *level = -1; *error = 0; ERR_clear_error(); if((ctx = X509_STORE_CTX_new()) != NULL){ @@ -3266,7 +3326,7 @@ get_chain_for_cert(X509 *cert, int *error) *error = X509_STORE_CTX_get_error(ctx); else if((chain = sk_X509_new_null()) != NULL){ for(x = cert; ; x = xtmp){ - if(++level > 0) + if(++*level > 0) sk_X509_push(chain, X509_dup(x)); rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x); if(rc < 0) @@ -3277,10 +3337,6 @@ get_chain_for_cert(X509 *cert, int *error) break; } } - if((*error && chain != NULL) || level == 0){ - sk_X509_pop_free(chain, X509_free); - chain = NULL; - } X509_STORE_CTX_free(ctx); } return chain; @@ -3292,22 +3348,37 @@ get_chain_for_cert(X509 *cert, int *error) * * This takes the header for the outgoing message as well as a pointer * to the current body (which may be reallocated). + * The last argument (BODY **bp) is an argument that tells Alpine + * if the body has 8 bit. if *bp is not null we compute two signatures + * one for the quoted-printable encoded message, and another for the + * 8bit encoded message. We return the signature for the 8bit encoded + * part in p2->body.mime.text.data. + * The reason why we compute two signatures is so that we can decide + * which one to use later, and we only do it in the case that *bp is + * not null. If we did not do this, then we might not be able to sign + * a message until we log in to the smtp server, so instead of doing + * that, we get ready for any possible situation we might find. */ int -sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) +sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp) { STORE_S *outs = NULL; + STORE_S *outs_2 = NULL; BODY *body = *bodyP; BODY *newBody = NULL; PART *p1 = NULL; PART *p2 = NULL; PERSONAL_CERT *pcert; BIO *in = NULL; + BIO *in_2 = NULL; BIO *out = NULL; + BIO *out_2 = NULL; PKCS7 *p7 = NULL; + PKCS7 *p7_2 = NULL; STACK_OF(X509) *chain; int result = 0, error; int flags = dont_detach ? 0 : PKCS7_DETACHED; + int level; dprint((9, "sign_outgoing_message()")); @@ -3332,13 +3403,43 @@ sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) if(!pcert->key) goto end; - - in = body_to_bio(body); - chain = get_chain_for_cert(pcert->cert, &error); + if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error) + || level == 0){ + sk_X509_pop_free(chain, X509_free); + chain = NULL; + } + + if(error) + q_status_message(SM_ORDER, 1, 1, + _("Not all certificates needed to verify signature included in signed message")); + + in = body_to_bio(body); p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags); + if(bp && *bp){ + int i, save_encoding; + + for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++); + + if(i > ENCMAX){ /* no empty encoding slots! */ + *bp = NULL; + } + else { + save_encoding = (*bp)->encoding; + body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT]; + + in_2 = body_to_bio(body); + + body_encodings[i] = NULL; + (*bp)->encoding = save_encoding; + } + } + + if(bp && *bp) + p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags); + if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global)) forget_private_keys(); @@ -3350,9 +3451,6 @@ sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) goto end; } - if(error) - q_status_message(SM_ORDER, 1, 1, _("Not all certificates needed to verify signature included in signed message")); - outs = so_get(BioType, NULL, EDIT_ACCESS); out = bio_from_store(outs); @@ -3360,6 +3458,16 @@ sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) (void) BIO_flush(out); so_seek(outs, 0, SEEK_SET); + + if(bp && *bp && p7_2){ + outs_2 = so_get(BioType, NULL, EDIT_ACCESS); + out_2 = bio_from_store(outs_2); + + i2d_PKCS7_bio(out_2, p7_2); + (void) BIO_flush(out_2); + + so_seek(outs_2, 0, SEEK_SET); + } if((flags&PKCS7_DETACHED)==0){ @@ -3408,6 +3516,7 @@ sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) p1->next = p2; setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL); + p2->body.mime.text.data = (unsigned char *) outs_2; p2->body.contents.text.data = (unsigned char *) outs; newBody->nested.part = p1; @@ -3422,6 +3531,11 @@ end: PKCS7_free(p7); BIO_free(in); + if(bp && *bp){ + if(p7_2) PKCS7_free(p7_2); + BIO_free(in_2); + } + dprint((9, "sign_outgoing_message returns %d", result)); return result; } diff --git a/pith/smime.h b/pith/smime.h index a2d2c805..f917daf4 100644 --- a/pith/smime.h +++ b/pith/smime.h @@ -50,7 +50,7 @@ int is_pkcs7_body(BODY *b); int fiddle_smime_message(BODY *b, long msgno); int encrypt_outgoing_message(METAENV *header, BODY **bodyP); void free_smime_body_sparep(void **sparep); -int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach); +int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp); void gf_puts_uline(char *txt, gf_io_t pc); PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri); PERSONAL_CERT *get_personal_certs(char *path); @@ -84,6 +84,7 @@ int add_file_to_container(WhichCerts ctype, char *fpath, char *altname); void *create_smime_sparep(SpareType stype, void *s); SpareType get_smime_sparep_type(void *s); void *get_smime_sparep_data(void *s); +STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error, int *level); #endif /* PITH_SMIME_INCLUDED */ #endif /* SMIME */ diff --git a/pith/smkeys.c b/pith/smkeys.c index 23c9d21a..18ed6f1b 100644 --- a/pith/smkeys.c +++ b/pith/smkeys.c @@ -46,8 +46,27 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt static char *emailstrclean(char *string); static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup); +/* given a certificate and an email address, add the + * extension and md5 key to the name. return an allocated + * name, freed by caller + */ +char * +smime_name(char *email, X509 *x, WhichCerts ctype) +{ + char bufx[256]; + char *rv; + + snprintf(bufx, sizeof(bufx), "%08lx",X509_subject_name_hash(x)); + rv = cpystr(bufx); +// get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), NULL); +// rv = fs_get(strlen(email) + 4 + 2 + strlen(bufx) + 1); +// sprintf(rv, "%s%s.%s", email, EXTCERT(ctype), bufx); + + return rv; +} + void -get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen) +get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s) { unsigned char md[128]; char *b; @@ -63,8 +82,8 @@ get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen) if(b-buf+3>=maxLen) break; - if(i != 0) - *b++ = ':'; + if(i != 0 && s && *s) + *b++ = *s; snprintf(b, maxLen - (b-buf), "%02x", md[i]); b+=2; diff --git a/pith/smkeys.h b/pith/smkeys.h index 0781a018..0fe8faad 100644 --- a/pith/smkeys.h +++ b/pith/smkeys.h @@ -57,9 +57,10 @@ void add_to_end_of_certlist(CertList **cl, char *name, X509 *cert); void free_certlist(CertList **cl); PERSONAL_CERT *mem_to_personal_certs(char *contents); void free_personal_certs(PERSONAL_CERT **pc); -void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen); +void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s); int certlist_to_file(char *filename, CertList *certlist); int load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert); +char *smime_name(char *email, X509 *x, WhichCerts ctype); #endif /* PITH_SMKEYS_INCLUDED */ |