/* pk7_mime.c */
/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
- * project 1999.
+ * project.
*/
/* ====================================================================
- * Copyright (c) 1999 The OpenSSL Project. All rights reserved.
+ * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
DECLARE_STACK_OF(MIME_HEADER)
IMPLEMENT_STACK_OF(MIME_HEADER)
+static int pkcs7_output_data(BIO *bio, BIO *data, PKCS7 *p7, int flags);
static int B64_write_PKCS7(BIO *bio, PKCS7 *p7);
static PKCS7 *B64_read_PKCS7(BIO *bio);
static char * strip_ends(char *name);
static void mime_param_free(MIME_PARAM *param);
static int mime_bound_check(char *line, int linelen, char *bound, int blen);
static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
-static int iscrlf(char c);
+static int strip_eol(char *linebuf, int *plen);
static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
static void mime_hdr_free(MIME_HEADER *hdr);
#define MAX_SMLEN 1024
#define mime_debug(x) /* x */
-
-typedef void (*stkfree)();
-
/* Base 64 read and write of PKCS#7 structure */
static int B64_write_PKCS7(BIO *bio, PKCS7 *p7)
return p7;
}
+/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
+
+static int pk7_write_micalg(BIO *out, PKCS7 *p7)
+ {
+ STACK_OF(X509_ALGOR) *mdalgs;
+ const EVP_MD *md;
+ int i, have_unknown = 0, write_comma, ret = 0, md_nid;
+ mdalgs = p7->d.sign->md_algs;
+ have_unknown = 0;
+ write_comma = 0;
+ for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++)
+ {
+ if (write_comma)
+ BIO_write(out, ",", 1);
+ write_comma = 1;
+ md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm);
+ md = EVP_get_digestbynid(md_nid);
+ if (md && md->md_ctrl)
+ {
+ int rv;
+ char *micstr;
+ rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr);
+ if (rv > 0)
+ {
+ BIO_puts(out, micstr);
+ OPENSSL_free(micstr);
+ continue;
+ }
+ if (rv != -2)
+ goto err;
+ }
+ switch(md_nid)
+ {
+ case NID_sha1:
+ BIO_puts(out, "sha1");
+ break;
+
+ case NID_md5:
+ BIO_puts(out, "md5");
+ break;
+
+ case NID_sha256:
+ BIO_puts(out, "sha-256");
+ break;
+
+ case NID_sha384:
+ BIO_puts(out, "sha-384");
+ break;
+
+ case NID_sha512:
+ BIO_puts(out, "sha-512");
+ break;
+
+ case NID_id_GostR3411_94:
+ BIO_puts(out, "gostr3411-94");
+ goto err;
+ break;
+
+ default:
+ if (have_unknown)
+ write_comma = 0;
+ else
+ {
+ BIO_puts(out, "unknown");
+ have_unknown = 1;
+ }
+ break;
+
+ }
+ }
+
+ ret = 1;
+ err:
+
+ return ret;
+
+ }
+
+
+
+
/* SMIME sender */
int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
{
- char linebuf[MAX_SMLEN];
char bound[33], c;
int i;
+ char *mime_prefix, *mime_eol, *msg_type=NULL;
+ if (flags & PKCS7_NOOLDMIMETYPE)
+ mime_prefix = "application/pkcs7-";
+ else
+ mime_prefix = "application/x-pkcs7-";
+
+ if (flags & PKCS7_CRLFEOL)
+ mime_eol = "\r\n";
+ else
+ mime_eol = "\n";
if((flags & PKCS7_DETACHED) && data) {
/* We want multipart/signed */
/* Generate a random boundary */
bound[i] = c;
}
bound[32] = 0;
- BIO_printf(bio, "MIME-Version: 1.0\r\n");
+ BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
BIO_printf(bio, "Content-Type: multipart/signed;");
- BIO_printf(bio, " protocol=\"application/x-pkcs7-signature\";");
- BIO_printf(bio, " micalg=sha1; boundary=\"----%s\"\r\n\r\n", bound);
- BIO_printf(bio, "This is an S/MIME signed message\n\n");
+ BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
+ BIO_puts(bio, " micalg=\"");
+ pk7_write_micalg(bio, p7);
+ BIO_printf(bio, "\"; boundary=\"----%s\"%s%s",
+ bound, mime_eol, mime_eol);
+ BIO_printf(bio, "This is an S/MIME signed message%s%s",
+ mime_eol, mime_eol);
/* Now write out the first part */
- BIO_printf(bio, "------%s\r\n", bound);
- if(flags & PKCS7_TEXT) BIO_printf(bio, "Content-Type: text/plain\r\n\r\n");
- while((i = BIO_read(data, linebuf, MAX_SMLEN)) > 0)
- BIO_write(bio, linebuf, i);
- BIO_printf(bio, "\r\n------%s\r\n", bound);
+ BIO_printf(bio, "------%s%s", bound, mime_eol);
+ pkcs7_output_data(bio, data, p7, flags);
+ BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
/* Headers for signature */
- BIO_printf(bio, "Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\r\n");
- BIO_printf(bio, "Content-Transfer-Encoding: base64\r\n");
- BIO_printf(bio, "Content-Disposition: attachment; filename=\"smime.p7s\"\r\n\r\n");
+ BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
+ BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
+ BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
+ mime_eol);
+ BIO_printf(bio, "Content-Disposition: attachment;");
+ BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
+ mime_eol, mime_eol);
B64_write_PKCS7(bio, p7);
- BIO_printf(bio,"\r\n------%s--\r\n\r\n", bound);
+ BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
+ mime_eol, mime_eol);
return 1;
}
+
+ /* Determine smime-type header */
+
+ if (PKCS7_type_is_enveloped(p7))
+ msg_type = "enveloped-data";
+ else if (PKCS7_type_is_signed(p7))
+ {
+ /* If we have any signers it is signed-data otherwise
+ * certs-only.
+ */
+ STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
+ sinfos = PKCS7_get_signer_info(p7);
+ if (sk_PKCS7_SIGNER_INFO_num(sinfos) > 0)
+ msg_type = "signed-data";
+ else
+ msg_type = "certs-only";
+ }
/* MIME headers */
- BIO_printf(bio, "MIME-Version: 1.0\r\n");
- BIO_printf(bio, "Content-Disposition: attachment; filename=\"smime.p7m\"\r\n");
- BIO_printf(bio, "Content-Type: application/x-pkcs7-mime; name=\"smime.p7m\"\r\n");
- BIO_printf(bio, "Content-Transfer-Encoding: base64\r\n\r\n");
+ BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
+ BIO_printf(bio, "Content-Disposition: attachment;");
+ BIO_printf(bio, " filename=\"smime.p7m\"%s", mime_eol);
+ BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
+ if (msg_type)
+ BIO_printf(bio, " smime-type=%s;", msg_type);
+ BIO_printf(bio, " name=\"smime.p7m\"%s", mime_eol);
+ BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
+ mime_eol, mime_eol);
B64_write_PKCS7(bio, p7);
- BIO_printf(bio, "\r\n");
+ BIO_printf(bio, "%s", mime_eol);
return 1;
}
+/* Handle output of PKCS#7 data */
+
+
+static int pkcs7_output_data(BIO *out, BIO *data, PKCS7 *p7, int flags)
+ {
+ BIO *tmpbio, *p7bio;
+
+ if (!(flags & PKCS7_STREAM))
+ {
+ SMIME_crlf_copy(data, out, flags);
+ return 1;
+ }
+
+ /* Partial sign operation */
+
+ /* Initialize sign operation */
+ p7bio = PKCS7_dataInit(p7, out);
+
+ /* Copy data across, computing digests etc */
+ SMIME_crlf_copy(data, p7bio, flags);
+
+ /* Must be detached */
+ PKCS7_set_detached(p7, 1);
+
+ /* Finalize signatures */
+ PKCS7_dataFinal(p7, p7bio);
+
+ /* Now remove any digests prepended to the BIO */
+
+ while (p7bio != out)
+ {
+ tmpbio = BIO_pop(p7bio);
+ BIO_free(p7bio);
+ p7bio = tmpbio;
+ }
+
+ return 1;
+
+ }
+
/* SMIME reader: handle multipart/signed and opaque signing.
* in multipart case the content is placed in a memory BIO
* pointed to by "bcont". In opaque this is set to NULL
BIO_write(out, linebuf, len);
return 1;
}
- if(flags & PKCS7_TEXT) BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
+ if(flags & PKCS7_TEXT)
+ BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) {
- eol = 0;
- while(iscrlf(linebuf[len - 1])) {
- len--;
- eol = 1;
- }
- BIO_write(out, linebuf, len);
+ eol = strip_eol(linebuf, &len);
+ if (len)
+ BIO_write(out, linebuf, len);
if(eol) BIO_write(out, "\r\n", 2);
}
return 1;
{
char linebuf[MAX_SMLEN];
int len, blen;
+ int eol = 0, next_eol = 0;
BIO *bpart = NULL;
STACK_OF(BIO) *parts;
char state, part, first;
sk_BIO_push(parts, bpart);
return 1;
} else if(part) {
+ /* Strip CR+LF from linebuf */
+ next_eol = strip_eol(linebuf, &len);
if(first) {
first = 0;
if(bpart) sk_BIO_push(parts, bpart);
bpart = BIO_new(BIO_s_mem());
-
- } else BIO_write(bpart, "\r\n", 2);
- /* Strip CR+LF from linebuf */
- while(iscrlf(linebuf[len - 1])) len--;
- BIO_write(bpart, linebuf, len);
+ BIO_set_mem_eof_return(bpart, 0);
+ } else if (eol)
+ BIO_write(bpart, "\r\n", 2);
+ eol = next_eol;
+ if (len)
+ BIO_write(bpart, linebuf, len);
}
}
return 0;
}
-static int iscrlf(char c)
-{
- if(c == '\r' || c == '\n') return 1;
- return 0;
-}
-
/* This is the big one: parse MIME header lines up to message body */
#define MIME_INVALID 0
}
return 0;
}
+
+static int strip_eol(char *linebuf, int *plen)
+ {
+ int len = *plen;
+ char *p, c;
+ int is_eol = 0;
+ p = linebuf + len - 1;
+ for (p = linebuf + len - 1; len > 0; len--, p--)
+ {
+ c = *p;
+ if (c == '\n')
+ is_eol = 1;
+ else if (c != '\r')
+ break;
+ }
+ *plen = len;
+ return is_eol;
+ }