summaryrefslogtreecommitdiff
path: root/pith/smime.c
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2014-05-02 19:04:29 -0600
committerEduardo Chappa <chappa@washington.edu>2014-05-02 19:04:29 -0600
commit223b392e8d40a0622936403d1da5eaf0cdd54d60 (patch)
tree05858ceae7dea53c9e97c205ce524a112f41723e /pith/smime.c
parent0d1e49834f4c3005b2b92a4ce8a03bd395d2fb1e (diff)
downloadalpine-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.c206
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;