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 int B64_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *in, int flags);
static PKCS7 *B64_read_PKCS7(BIO *bio);
static char * strip_ends(char *name);
static char * strip_start(char *name);
#define MAX_SMLEN 1024
#define mime_debug(x) /* x */
+/* Output a PKCS#7 structure in BER format streaming if necessary */
+
+int i2d_PKCS7_bio_stream(BIO *out, PKCS7 *p7, BIO *in, int flags)
+ {
+ /* If streaming create stream BIO and copy all content through it */
+ if (flags & PKCS7_STREAM)
+ {
+ BIO *bio, *tbio;
+ bio = BIO_new_PKCS7(out, p7);
+ if (!bio)
+ {
+ PKCS7err(PKCS7_F_PKCS7_BIO_STREAM,ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ SMIME_crlf_copy(in, bio, flags);
+ BIO_flush(bio);
+ /* Free up successive BIOs until we hit the old output BIO */
+ do
+ {
+ tbio = BIO_pop(bio);
+ BIO_free(bio);
+ bio = tbio;
+ } while (bio != out);
+ }
+ /* else just write out PKCS7 structure which will have all content
+ * stored internally
+ */
+ else
+ i2d_PKCS7_bio(out, p7);
+ return 1;
+ }
+
/* Base 64 read and write of PKCS#7 structure */
-static int B64_write_PKCS7(BIO *bio, PKCS7 *p7)
-{
+static int B64_write_PKCS7(BIO *out, PKCS7 *p7, BIO *in, int flags)
+ {
BIO *b64;
- if(!(b64 = BIO_new(BIO_f_base64()))) {
+ int r;
+ b64 = BIO_new(BIO_f_base64());
+ if(!b64)
+ {
PKCS7err(PKCS7_F_B64_WRITE_PKCS7,ERR_R_MALLOC_FAILURE);
return 0;
- }
- bio = BIO_push(b64, bio);
- i2d_PKCS7_bio(bio, p7);
- BIO_flush(bio);
- bio = BIO_pop(bio);
+ }
+ /* prepend the b64 BIO so all data is base64 encoded.
+ */
+ out = BIO_push(b64, out);
+ r = i2d_PKCS7_bio_stream(out, p7, in, flags);
+ BIO_flush(out);
+ BIO_pop(out);
BIO_free(b64);
- return 1;
-}
+ return r;
+ }
static PKCS7 *B64_read_PKCS7(BIO *bio)
{
return p7;
}
+/* Streaming PKCS#7 PEM write */
+
+int PEM_write_bio_PKCS7_stream(BIO *out, PKCS7 *p7, BIO *in, int flags)
+ {
+ int r;
+ BIO_puts(out, "-----BEGIN PKCS7-----\n");
+ r = B64_write_PKCS7(out, p7, in, flags);
+ BIO_puts(out, "-----END PKCS7-----\n");
+ return r;
+ }
+
/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
static int pk7_write_micalg(BIO *out, PKCS7 *p7)
{
STACK_OF(X509_ALGOR) *mdalgs;
- STACK *mic_sk;
- int i, have_unknown = 0, ret = 0;
+ const EVP_MD *md;
+ int i, have_unknown = 0, write_comma, ret = 0, md_nid;
mdalgs = p7->d.sign->md_algs;
- mic_sk = sk_new_null();
- if (!mic_sk)
- goto err;
have_unknown = 0;
+ write_comma = 0;
for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++)
{
- switch(OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm))
+ 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)
{
- case NID_sha1:
- if (!sk_push(mic_sk, "sha1"))
+ 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:
- if (!sk_push(mic_sk, "md5"))
- goto err;
+ BIO_puts(out, "md5");
break;
case NID_sha256:
- if (!sk_push(mic_sk, "sha-256"))
- goto err;
+ BIO_puts(out, "sha-256");
break;
case NID_sha384:
- if (!sk_push(mic_sk, "sha-384"))
- goto err;
+ BIO_puts(out, "sha-384");
break;
case NID_sha512:
- if (!sk_push(mic_sk, "sha-512"))
- goto err;
+ BIO_puts(out, "sha-512");
break;
case NID_id_GostR3411_94:
- if (!sk_push(mic_sk, "gostr3411-94"))
+ BIO_puts(out, "gostr3411-94");
goto err;
break;
default:
- if (!have_unknown)
+ if (have_unknown)
+ write_comma = 0;
+ else
{
- if (!sk_push(mic_sk, "unknown"))
- goto err;
+ BIO_puts(out, "unknown");
have_unknown = 1;
}
break;
}
}
- for (i = 0; i < sk_num(mic_sk); i++)
- {
- BIO_puts(out, sk_value(mic_sk, i));
- if (i > 0)
- BIO_write(out, ",", 1);
- }
ret = 1;
err:
- if (mic_sk)
- sk_free(mic_sk);
-
return ret;
}
BIO_printf(bio, "Content-Disposition: attachment;");
BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
mime_eol, mime_eol);
- B64_write_PKCS7(bio, p7);
+ B64_write_PKCS7(bio, p7, NULL, 0);
BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
mime_eol, mime_eol);
return 1;
else
msg_type = "certs-only";
}
+ else
+ flags &= ~PKCS7_STREAM;
/* MIME headers */
BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
BIO_printf(bio, "Content-Disposition: attachment;");
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);
+ B64_write_PKCS7(bio, p7, data, flags);
BIO_printf(bio, "%s", mime_eol);
return 1;
}
/* Copy text from one BIO to another making the output CRLF at EOL */
int SMIME_crlf_copy(BIO *in, BIO *out, int flags)
{
+ BIO *bf;
char eol;
int len;
char linebuf[MAX_SMLEN];
- if(flags & PKCS7_BINARY) {
+ /* Buffer output so we don't write one line at a time. This is
+ * useful when streaming as we don't end up with one OCTET STRING
+ * per line.
+ */
+ bf = BIO_new(BIO_f_buffer());
+ if (!bf)
+ return 0;
+ out = BIO_push(bf, out);
+ if(flags & PKCS7_BINARY)
+ {
while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
BIO_write(out, linebuf, len);
- return 1;
- }
- 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 = strip_eol(linebuf, &len);
- if (len)
- BIO_write(out, linebuf, len);
- if(eol) BIO_write(out, "\r\n", 2);
- }
+ }
+ else
+ {
+ 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 = strip_eol(linebuf, &len);
+ if (len)
+ BIO_write(out, linebuf, len);
+ if(eol) BIO_write(out, "\r\n", 2);
+ }
+ }
+ BIO_flush(out);
+ BIO_pop(out);
+ BIO_free(bf);
return 1;
}