diff options
author | Eduardo Chappa <chappa@washington.edu> | 2014-05-02 19:04:29 -0600 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2014-05-02 19:04:29 -0600 |
commit | 223b392e8d40a0622936403d1da5eaf0cdd54d60 (patch) | |
tree | 05858ceae7dea53c9e97c205ce524a112f41723e /pith/smime.c | |
parent | 0d1e49834f4c3005b2b92a4ce8a03bd395d2fb1e (diff) | |
download | alpine-223b392e8d40a0622936403d1da5eaf0cdd54d60.tar.xz |
* Create help for explaining how encrypted password file support
works.
* When a message is sent encrypted, add the sender certificate so that
the sender can decrypt it too.
* When a message is signed and encrypted, first sign it and then encrypt
it. This changes the usual order of encrypting and then signing, and it has
the shortcoming of making bigger messages. However, this is the way that
most clients work with S/MIME, and so for compatibility with other programs,
we will send signed, then encrypted, instead of encrypted, then signed.
Hmm... should we sign the encrypted part?
* Avoid the first RSET smtp command, as this causes delays in some evily
managed servers.
Diffstat (limited to 'pith/smime.c')
-rw-r--r-- | pith/smime.c | 206 |
1 files changed, 169 insertions, 37 deletions
diff --git a/pith/smime.c b/pith/smime.c index 566e2be5..ce7b6a70 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -56,18 +56,19 @@ static int load_private_key(PERSONAL_CERT *pcert); static void create_local_cache(char *h, char *base, BODY *b, int type); static long rfc822_output_func(void *b, char *string); static void setup_pkcs7_body_for_signature(BODY *b, char *description, - char *type, char *filename); + char *type, char *filename, char *smime_type); static BIO *body_to_bio(BODY *body); 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); -static int do_detached_signature_verify(BODY *b, long msgno, char *section); +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); static void setup_storage_locations(void); static int copy_container_to_dir(WhichCerts which); +static int do_fiddle_smime_message(BODY *b, long msgno, char *section); void setup_privatekey_storage(void); int smime_path(char *rpath, char *fpath, size_t len); int smime_extract_and_save_cert(PKCS7 *p7); @@ -89,6 +90,36 @@ 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) +{ + SMIME_SPARE_S *rv; + + rv = fs_get(sizeof(SMIME_SPARE_S)); + rv->sptype = stype; + rv->data = s; + return (void *) rv; +} + +SpareType +get_smime_sparep_type(void *s) +{ + return ((SMIME_SPARE_S *)s)->sptype; +} + +void * +get_smime_sparep_data(void *s) +{ + return ((SMIME_SPARE_S *)s)->data; +} + #ifdef PASSFILE /* @@ -2041,7 +2072,7 @@ load_private_key(PERSONAL_CERT *pcert) static void -setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename) +setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type) { b->type = TYPEAPPLICATION; b->subtype = cpystr(type); @@ -2052,6 +2083,8 @@ setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *fil set_parameter(&b->disposition.parameter, "filename", filename); set_parameter(&b->parameter, "name", filename); + if(smime_type && *smime_type) + set_parameter(&b->parameter, "smime-type", smime_type); } @@ -2238,6 +2271,8 @@ encrypt_outgoing_message(METAENV *header, BODY **bodyP) BODY *body = *bodyP; BODY *newBody = NULL; int result = 0; + X509 *cert; + char buf[MAXPATH]; dprint((9, "encrypt_outgoing_message()")); smime_init(); @@ -2250,13 +2285,9 @@ encrypt_outgoing_message(METAENV *header, BODY **bodyP) 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, Public); - if(cert) + if((cert = get_cert_for(buf, Public)) != NULL) sk_X509_push(encerts,cert); else{ q_status_message2(SM_ORDER, 1, 1, @@ -2267,6 +2298,14 @@ encrypt_outgoing_message(METAENV *header, BODY **bodyP) } } + /* add the sender's certificate so that they can decrypt the message too */ + for(a=header->env->from; a ; a = a->next){ + snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host); + + if((cert = get_cert_for(buf, Public)) != NULL + && sk_X509_find(encerts, cert) == -1) + sk_X509_push(encerts,cert); + } in = body_to_bio(body); @@ -2491,12 +2530,74 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent) void free_smime_body_sparep(void **sparep) { + char *s; + SIZEDTEXT *st; if(sparep && *sparep){ - PKCS7_free((PKCS7 *) (*sparep)); - *sparep = NULL; + switch(get_smime_sparep_type(*sparep)){ + case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep)); + break; + case CharType: s = (char *)get_smime_sparep_data(*sparep); + fs_give((void **) &s); + break; + case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep); + fs_give((void **) &st->data); + fs_give((void **) &st); + break; + default : break; + } + ((SMIME_SPARE_S *)(*sparep))->data = NULL; + fs_give(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. @@ -2569,7 +2670,7 @@ free_smime_body_sparep(void **sparep) * Given a multipart body of type multipart/signed, attempt to verify it. * Returns non-zero if the body was changed. */ -static int +int do_detached_signature_verify(BODY *b, long msgno, char *section) { PKCS7 *p7 = NULL; @@ -2579,29 +2680,44 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) unsigned long mimelen, bodylen; char newSec[100], *mimetext, *bodytext; char *what_we_did; + SIZEDTEXT *st; dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL")); smime_init(); - snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : ""); - - mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0); - - if(mimetext) - bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0); + /* if it was signed and then encrypted, use the decrypted text + * to check the validity of the signature + */ + if(b->sparep){ + if(get_smime_sparep_type(b->sparep) == SizedText){ + /* bodytext includes mimetext */ + st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep); + bodytext = st->data; + bodylen = st->size; + mimetext = NULL; + mimelen = 0L; + } + } + else{ + snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : ""); + mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0); + if(mimetext) + bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0); - if (mimetext == NULL || bodytext == NULL) - return modified_the_body; + if(mimetext == NULL || bodytext == NULL) + return modified_the_body; + } snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : ""); - if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL - || (in = BIO_new(BIO_s_mem())) == NULL) - return modified_the_body; + if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL + || (in = BIO_new(BIO_s_mem())) == NULL) + return modified_the_body; (void) BIO_reset(in); - BIO_write(in, mimetext, mimelen); + if(mimetext != NULL) + BIO_write(in, mimetext, mimelen); BIO_write(in, bodytext, bodylen); /* Try compatibility with the past and check if this message @@ -2623,6 +2739,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) * it by hand. */ if((result = do_signature_verify(p7, in, NULL, 1)) == 0 + && mimelen > 0 /* do not do this for encrypted messages */ && IS_REMOTE(ps_global->mail_stream->mailbox)){ char *fetch; unsigned long hlen, tlen; @@ -2645,7 +2762,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) bstart += 4; INIT(&bs, mail_string, bstart, tlen); - rfc822_parse_msg_full(&env, &body, h, (bstart-h), &bs, BADHOST, 0, 0); + rfc822_parse_msg_full(&env, &body, h, bstart-h, &bs, BADHOST, 0, 0); mail_free_envelope(&env); mail_free_body_part(&b->nested.part); @@ -2654,6 +2771,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) modified_the_body = 1; snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : ""); + mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0); if(mimetext) @@ -2701,7 +2819,7 @@ do_detached_signature_verify(BODY *b, long msgno, char *section) b->description = cpystr(what_we_did); - b->sparep = p7; + b->sparep = create_smime_sparep(P7Type, p7); p = b->nested.part; @@ -2844,11 +2962,11 @@ do_decoding(BODY *b, long msgno, const char *section) * Extract binary data from part to an in-memory store */ - if(b->sparep){ /* already done */ - p7 = (PKCS7*) b->sparep; + if(b->sparep){ + if(get_smime_sparep_type(b->sparep) == P7Type) + p7 = (PKCS7*) get_smime_sparep_data(b->sparep); } else{ - p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1"); if(!p7){ q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s", @@ -2860,7 +2978,7 @@ do_decoding(BODY *b, long msgno, const char *section) * 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->sparep = create_smime_sparep(P7Type, p7); } dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7))); @@ -2927,8 +3045,7 @@ do_decoding(BODY *b, long msgno, const char *section) if(!decrypt_result){ q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"), (char*) openssl_error_string()); - goto end; - } + goto end; } BIO_write(out, null, 1); } @@ -2957,12 +3074,24 @@ do_decoding(BODY *b, long msgno, const char *section) q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed.")); } else{ + SIZEDTEXT *st; 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); + rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0); mail_free_envelope(&env); /* Don't care about this */ + if(body->type == TYPEMULTIPART + && !strucmp(body->subtype, "SIGNED")){ + 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); + } body->mime.offset = 0; body->mime.text.size = 0; @@ -3094,7 +3223,6 @@ do_fiddle_smime_message(BODY *b, long msgno, char *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); @@ -3141,6 +3269,7 @@ get_chain_for_cert(X509 *cert, int *error) X509_STORE_CTX *ctx; X509 *x, *xtmp; int rc; /* return code */ + int level = -1; *error = 0; ERR_clear_error(); @@ -3150,7 +3279,8 @@ 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){ - sk_X509_push(chain, X509_dup(x)); + if(++level > 0) + sk_X509_push(chain, X509_dup(x)); rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x); if(rc < 0) *error = 1; @@ -3160,8 +3290,10 @@ get_chain_for_cert(X509 *cert, int *error) break; } } - if(*error && chain != NULL) + if((*error && chain != NULL) || level == 0){ sk_X509_pop_free(chain, X509_free); + chain = NULL; + } X509_STORE_CTX_free(ctx); } return chain; @@ -3248,7 +3380,7 @@ sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach) newBody = mail_newbody(); - setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m"); + setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data"); newBody->contents.text.data = (unsigned char *) outs; *bodyP = newBody; @@ -3288,7 +3420,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"); + setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL); p2->body.contents.text.data = (unsigned char *) outs; newBody->nested.part = p1; |