summaryrefslogtreecommitdiff
path: root/pith/smime.c
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2014-05-17 16:50:01 -0600
committerEduardo Chappa <chappa@washington.edu>2014-05-17 16:50:01 -0600
commit0b9aa5eb03014bb55211186ba0ce60b2a8986ba0 (patch)
tree0b5c4f9861aa883b0914c00385d8fd480c528044 /pith/smime.c
parent223b392e8d40a0622936403d1da5eaf0cdd54d60 (diff)
downloadalpine-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.c196
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;