diff options
author | Eduardo Chappa <chappa@washington.edu> | 2014-05-17 16:50:01 -0600 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2014-05-17 16:50:01 -0600 |
commit | 0b9aa5eb03014bb55211186ba0ce60b2a8986ba0 (patch) | |
tree | 0b5c4f9861aa883b0914c00385d8fd480c528044 /pith/smime.c | |
parent | 223b392e8d40a0622936403d1da5eaf0cdd54d60 (diff) | |
download | alpine-0b9aa5eb03014bb55211186ba0ce60b2a8986ba0.tar.xz |
* New version 2.19.999
* Introduce the option "Validate Using Certificate Store Only", and
make it the default. This will make Alpine check for the validity of
signatures in certificates that a user has installed in their
system, and not in the certificates that come with the message. A
user can override this, although is not recommended, by disabling
this feature.
* When viewing a signed message, the ^E command would present an
empty screen or Alpine would crash because when Alpine would get the
PKCS7 body of the message from body->sparep, it would not decode it
properly due to the new way in which the sparep pointer is encoded
that was introduced in version 2.19.991.
* When a signed message is forwarded, the message might not be filtered
correctly, and mime information might make it into the body of the
forwarded message. In order to produce this, the message must be
forwarded from the index screen and not be opened. The reason why
this makes a difference is because opening a signed message changes
its body structure. The reason why a person could forward a message
before reading it is because the person could already be aware of
the content of the message (e.g. the message is in the sent-mail
folder).
* When a message fails to validate and the body is saved from the server
for validation, be careful in the way that body part pointers are
set, in order to do this we split the mail_body function into two
parts, one that gets the body, the other that gets the section of
the body. The new function that gets the section of the body
(mail_body_section), is used to assign pointers of the reconstructed
new body.
* When a container has not been defined, transferring messages to a
container will succeed, and the name of the container will be
written on screen.
* When Alpine is receiving the envelopes from an imap server, it
attempts to generate the index line immediately; while doing so it
might need to compute a score, and for this, it might need to go
back and do some operation in the same imap server. In this case,
Alpine will crash with a "lock when already locked" message. In
order to avoid this crash, a new check in match_pattern was added to
Alpine to avoid the second trip to a server that is busy sending us
envelopes. Reported by Peter Koellner.
* Update copyright notice in mswin.rc and pmapi.rc, as well as first
time user notice and special request notice.
* Alpine cannot handle correctly some characters in the Windows-1256
character set, which might lead to a crash or a corruption in the
screen. Work was done to contain the bug. A more complete fix will be
done in a future release. Reported by Professor Robert Funnell.
* Decode the name of attachment names, so they can be written as part
of the description of the part.
* When transferring certificates to a local container, create container
with default names PublicContainer, PrivateContainer and CAContainer,
as appropriate for these files, unless the user has provided some
other names.
Diffstat (limited to 'pith/smime.c')
-rw-r--r-- | pith/smime.c | 196 |
1 files changed, 89 insertions, 107 deletions
diff --git a/pith/smime.c b/pith/smime.c index ce7b6a70..6c49fdcc 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -62,7 +62,7 @@ static BIO *bio_from_store(STORE_S *store); static STORE_S *get_part_contents(long msgno, const char *section); static PKCS7 *get_pkcs7_from_part(long msgno, const char *section); static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent); -int do_detached_signature_verify(BODY *b, long msgno, char *section); +static int do_detached_signature_verify(BODY *b, long msgno, char *section); static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7); static int do_decoding(BODY *b, long msgno, const char *section); static void free_smime_struct(SMIME_STUFF_S **smime); @@ -90,13 +90,6 @@ static X509_STORE *s_cert_store; static int seeded = 0; static int egdsocket = 0; -typedef enum {P7Type, CharType, SizedText} SpareType; - -typedef struct smime_sparep_t { - SpareType sptype; - void *data; -} SMIME_SPARE_S; - void * create_smime_sparep(SpareType stype, void *s) { @@ -1410,13 +1403,15 @@ copy_dir_to_container(WhichCerts which, char *contents) int ret = 0; BIO *bio_out = NULL, *bio_in = NULL; char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096]; - char *tempfile = NULL; + char *tempfile = NULL, fpath[MAXPATH+1]; DIR *dirp; struct dirent *d; REMDATA_S *rd = NULL; char *configdir = NULL; char *configpath = NULL; + char *configcontainer = NULL; char *filesuffix = NULL; + char *ret_dir = NULL; dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?")); smime_init(); @@ -1429,16 +1424,19 @@ copy_dir_to_container(WhichCerts which, char *contents) if(which == Public){ configdir = ps_global->VAR_PUBLICCERT_DIR; configpath = ps_global->smime->publicpath; + configcontainer = cpystr(DF_PUBLIC_CONTAINER); filesuffix = ".crt"; } else if(which == Private){ configdir = ps_global->VAR_PRIVATEKEY_DIR; configpath = ps_global->smime->privatepath; + configcontainer = cpystr(DF_PRIVATE_CONTAINER); filesuffix = ".key"; } else if(which == CACert){ configdir = ps_global->VAR_CACERT_DIR; configpath = ps_global->smime->capath; + configcontainer = cpystr(DF_CA_CONTAINER); filesuffix = ".crt"; } @@ -1534,7 +1532,7 @@ copy_dir_to_container(WhichCerts which, char *contents) * dstpath is either the local Container file or the local cache file * for the remote Container file. */ - tempfile = tempfile_in_same_dir(dstpath, "az", NULL); + tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir); } /* @@ -1609,12 +1607,24 @@ copy_dir_to_container(WhichCerts which, char *contents) BIO_free(bio_out); if(!ret){ - if(rename_file(tempfile, dstpath) < 0){ + if(ret_dir){ + if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath)) + snprintf(fpath, sizeof(fpath), "%s%c%s", + dstpath, tempfile[strlen(ret_dir)], configcontainer); + else + ret = -1; + } + else ret = -1; + + if(!ret){ + if(rename_file(tempfile, fpath) < 0){ q_status_message2(SM_ORDER, 3, 3, - _("Can't rename %s to %s"), tempfile, dstpath); + _("Can't rename %s to %s"), tempfile, fpath); ret = -1; + } else q_status_message1(SM_ORDER, 3, 3, + _("saved container to %s"), fpath); } - + /* if the container is remote, copy it */ if(!ret && IS_REMOTE(configpath)){ int e; @@ -1656,6 +1666,12 @@ copy_dir_to_container(WhichCerts which, char *contents) if(tempfile) fs_give((void **) &tempfile); + if(ret_dir) + fs_give((void **) &ret_dir); + + if(configcontainer) + fs_give((void **) &configcontainer); + return ret; } @@ -1921,36 +1937,6 @@ is_pkcs7_body(BODY *body) } -#ifdef notdef -/* - * Somewhat useful debug utility to dump the contents of a BIO to a file. - * Note that a memory BIO will have its contents eliminated after they - * are read so this will break the next step. - */ -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){ - if(BIO_method_type(in) != BIO_TYPE_MEM) - BIO_reset(in); - - while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) - BIO_write(out, iobuf, len); - - BIO_free(out); - } - - BIO_reset(in); -} -#endif - - /* * Recursively stash a pointer to the decrypted data in our * manufactured body. @@ -2255,7 +2241,7 @@ end: /* * Encrypt a message on the way out. Called from call_mailer in send.c - * The body may be reallocated. + * The body may be reallocated. */ int encrypt_outgoing_message(METAENV *header, BODY **bodyP) @@ -2287,9 +2273,9 @@ encrypt_outgoing_message(METAENV *header, BODY **bodyP) for(a=*pf->addr; a; a=a->next){ snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host); - if((cert = get_cert_for(buf, Public)) != NULL) + if((cert = get_cert_for(buf, Public)) != NULL){ sk_X509_push(encerts,cert); - else{ + }else{ q_status_message2(SM_ORDER, 1, 1, _("Unable to find certificate for <%s@%s>"), a->mailbox, a->host); @@ -2491,10 +2477,12 @@ static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent) { STACK_OF(X509) *otherCerts = NULL; + CertList *cl; int result; + int flags; const char *data; long err; - + if(!s_cert_store){ if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2, _("Couldn't verify S/MIME signature: No CA Certs were loaded")); @@ -2504,8 +2492,31 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent) smime_extract_and_save_cert(p7); - result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0); - + flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0; + + if(ps_global->smime->publiccertlist == NULL){ + renew_cert_data(&ps_global->smime->publiccertlist, Public); + for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){ + if(cl->x509_cert == NULL){ + char *s = strrchr(cl->name, '.'); + *s = '\0'; + cl->x509_cert = get_cert_for(cl->name, Public); + *s = '.'; + } + } + } + + if(ps_global->smime->publiccertlist){ + otherCerts = sk_X509_new_null(); + for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next) + if(cl->x509_cert != NULL) + sk_X509_push(otherCerts, X509_dup(cl->x509_cert)); + } + + result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags); + + sk_X509_pop_free(otherCerts, X509_free); + if(result){ q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok")); } @@ -2550,54 +2561,6 @@ free_smime_body_sparep(void **sparep) } } -/* return the mime header for this body part. Memory freed by caller */ -char * -smime_fetch_mime(BODY *b, MAILSTREAM *stream, long msgno, char * section, - unsigned long *mimelen) -{ - char *rv; - char newSec[100]; - - if(b->type == TYPEMULTIPART) - snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : ""); - else - strncpy(newSec, section, sizeof(newSec)); - newSec[sizeof(newSec)-1] = '\0'; - rv = mail_fetch_mime(stream, msgno, newSec, mimelen, 0); - return rv ? cpystr(rv) : NULL; -} - - -void -smime_write_body_header(BODY *b, MAILSTREAM *stream, long msgno, char *section, soutr_t f, void *s) -{ - char *rv; - char newSec[100]; - - if(b->nested.part == NULL) - return; - - if(b->nested.part->body.type == TYPEMULTIPART) - snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : ""); - else - strncpy(newSec, section, sizeof(newSec)); - newSec[sizeof(newSec)-1] = '\0'; - rv = mail_fetch_mime(stream, msgno, newSec, NULL, 0); - if(f && rv != NULL) - (*f)(s, rv); -} - -int -write_signed_body(BODY *b, MAILSTREAM *stream, long msgno, char *section, BIO *in) -{ - - smime_write_body_header(b, stream, msgno, section, rfc822_output_func, in); - - -} - - - /* Big comment, explaining the mess that exists out there, and how we deal with it, and also how we solve the problems that are created this way. @@ -2664,19 +2627,30 @@ write_signed_body(BODY *b, MAILSTREAM *stream, long msgno, char *section, BIO *i processed text. Nevertheless, at this time, this is automatic, and is causing a delay in the processing of the message, but it is validating correctly all messages. + + PART IV + + When the user sends a message as encrypted and signed, this code used to + encrypt first, and then sign the pkcs7 body, but it turns out that some + other clients can not handle these messages. While we could argue that the + other clients need to improve, we will support reading messages in both + ways, and will send messages using this technique; that is, signed first, + encrypted second. It seems that all tested clients support this way, so it + should be safe to do so. */ /* * Given a multipart body of type multipart/signed, attempt to verify it. * Returns non-zero if the body was changed. */ -int +static int do_detached_signature_verify(BODY *b, long msgno, char *section) { PKCS7 *p7 = NULL; BIO *in = NULL; PART *p; int result, modified_the_body = 0; + int flag; /* 1 silent, 0 not silent */ unsigned long mimelen, bodylen; char newSec[100], *mimetext, *bodytext; char *what_we_did; @@ -2693,7 +2667,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) if(get_smime_sparep_type(b->sparep) == SizedText){ /* bodytext includes mimetext */ st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep); - bodytext = st->data; + bodytext = (char *) st->data; bodylen = st->size; mimetext = NULL; mimelen = 0L; @@ -2724,7 +2698,9 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) * validates when we remove the last two characters. Silence * any failures first. */ - if(((result = do_signature_verify(p7, in, NULL, 1)) == 0) + 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; @@ -2738,7 +2714,9 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) * information. In this case, we fetch the message and we process * it by hand. */ - 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 && mimelen > 0 /* do not do this for encrypted messages */ && IS_REMOTE(ps_global->mail_stream->mailbox)){ char *fetch; @@ -2766,7 +2744,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) mail_free_envelope(&env); mail_free_body_part(&b->nested.part); - b->nested.part = body->nested.part; + b->nested.part = mail_body_section(body, section)->nested.part; create_local_cache(bstart, bstart, &b->nested.part->body, 1); modified_the_body = 1; @@ -3086,11 +3064,15 @@ do_decoding(BODY *b, long msgno, const char *section) char *cookie = NULL; PARAMETER *param; for (param = body->parameter; param && !cookie; param = param->next) - if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value; - st = fs_get(sizeof(SIZEDTEXT)); - st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */ - st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4); - body->sparep = create_smime_sparep(SizedText, (void *)st); + if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value; + if(cookie != NULL){ + st = fs_get(sizeof(SIZEDTEXT)); + st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */ + st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4); + body->sparep = create_smime_sparep(SizedText, (void *)st); + } + else + q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list.")); } body->mime.offset = 0; body->mime.text.size = 0; |