1e1051a39Sopenharmony_ci/* 2e1051a39Sopenharmony_ci * Copyright 2008-2022 The OpenSSL Project Authors. All Rights Reserved. 3e1051a39Sopenharmony_ci * 4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License"). You may not use 5e1051a39Sopenharmony_ci * this file except in compliance with the License. You can obtain a copy 6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at 7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html 8e1051a39Sopenharmony_ci */ 9e1051a39Sopenharmony_ci 10e1051a39Sopenharmony_ci#include <stdio.h> 11e1051a39Sopenharmony_ci#include "crypto/ctype.h" 12e1051a39Sopenharmony_ci#include "internal/cryptlib.h" 13e1051a39Sopenharmony_ci#include <openssl/rand.h> 14e1051a39Sopenharmony_ci#include <openssl/x509.h> 15e1051a39Sopenharmony_ci#include <openssl/asn1.h> 16e1051a39Sopenharmony_ci#include <openssl/asn1t.h> 17e1051a39Sopenharmony_ci#include <openssl/cms.h> 18e1051a39Sopenharmony_ci#include "crypto/evp.h" 19e1051a39Sopenharmony_ci#include "internal/bio.h" 20e1051a39Sopenharmony_ci#include "asn1_local.h" 21e1051a39Sopenharmony_ci 22e1051a39Sopenharmony_ci/* 23e1051a39Sopenharmony_ci * Generalised MIME like utilities for streaming ASN1. Although many have a 24e1051a39Sopenharmony_ci * PKCS7/CMS like flavour others are more general purpose. 25e1051a39Sopenharmony_ci */ 26e1051a39Sopenharmony_ci 27e1051a39Sopenharmony_ci/* 28e1051a39Sopenharmony_ci * MIME format structures Note that all are translated to lower case apart 29e1051a39Sopenharmony_ci * from parameter values. Quotes are stripped off 30e1051a39Sopenharmony_ci */ 31e1051a39Sopenharmony_ci 32e1051a39Sopenharmony_cistruct mime_param_st { 33e1051a39Sopenharmony_ci char *param_name; /* Param name e.g. "micalg" */ 34e1051a39Sopenharmony_ci char *param_value; /* Param value e.g. "sha1" */ 35e1051a39Sopenharmony_ci}; 36e1051a39Sopenharmony_ci 37e1051a39Sopenharmony_cistruct mime_header_st { 38e1051a39Sopenharmony_ci char *name; /* Name of line e.g. "content-type" */ 39e1051a39Sopenharmony_ci char *value; /* Value of line e.g. "text/plain" */ 40e1051a39Sopenharmony_ci STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */ 41e1051a39Sopenharmony_ci}; 42e1051a39Sopenharmony_ci 43e1051a39Sopenharmony_cistatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 44e1051a39Sopenharmony_ci const ASN1_ITEM *it); 45e1051a39Sopenharmony_cistatic char *strip_ends(char *name); 46e1051a39Sopenharmony_cistatic char *strip_start(char *name); 47e1051a39Sopenharmony_cistatic char *strip_end(char *name); 48e1051a39Sopenharmony_cistatic MIME_HEADER *mime_hdr_new(const char *name, const char *value); 49e1051a39Sopenharmony_cistatic int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value); 50e1051a39Sopenharmony_cistatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); 51e1051a39Sopenharmony_cistatic int mime_hdr_cmp(const MIME_HEADER *const *a, 52e1051a39Sopenharmony_ci const MIME_HEADER *const *b); 53e1051a39Sopenharmony_cistatic int mime_param_cmp(const MIME_PARAM *const *a, 54e1051a39Sopenharmony_ci const MIME_PARAM *const *b); 55e1051a39Sopenharmony_cistatic void mime_param_free(MIME_PARAM *param); 56e1051a39Sopenharmony_cistatic int mime_bound_check(char *line, int linelen, const char *bound, int blen); 57e1051a39Sopenharmony_cistatic int multi_split(BIO *bio, int flags, const char *bound, STACK_OF(BIO) **ret); 58e1051a39Sopenharmony_cistatic int strip_eol(char *linebuf, int *plen, int flags); 59e1051a39Sopenharmony_cistatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name); 60e1051a39Sopenharmony_cistatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name); 61e1051a39Sopenharmony_cistatic void mime_hdr_free(MIME_HEADER *hdr); 62e1051a39Sopenharmony_ci 63e1051a39Sopenharmony_ci#define MAX_SMLEN 1024 64e1051a39Sopenharmony_ci#define mime_debug(x) /* x */ 65e1051a39Sopenharmony_ci 66e1051a39Sopenharmony_ci/* Output an ASN1 structure in BER format streaming if necessary */ 67e1051a39Sopenharmony_ci 68e1051a39Sopenharmony_ci/* unfortunately cannot constify this due to CMS_stream() and PKCS7_stream() */ 69e1051a39Sopenharmony_ciint i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 70e1051a39Sopenharmony_ci const ASN1_ITEM *it) 71e1051a39Sopenharmony_ci{ 72e1051a39Sopenharmony_ci int rv = 1; 73e1051a39Sopenharmony_ci 74e1051a39Sopenharmony_ci /* If streaming create stream BIO and copy all content through it */ 75e1051a39Sopenharmony_ci if (flags & SMIME_STREAM) { 76e1051a39Sopenharmony_ci BIO *bio, *tbio; 77e1051a39Sopenharmony_ci bio = BIO_new_NDEF(out, val, it); 78e1051a39Sopenharmony_ci if (!bio) { 79e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); 80e1051a39Sopenharmony_ci return 0; 81e1051a39Sopenharmony_ci } 82e1051a39Sopenharmony_ci if (!SMIME_crlf_copy(in, bio, flags)) { 83e1051a39Sopenharmony_ci rv = 0; 84e1051a39Sopenharmony_ci } 85e1051a39Sopenharmony_ci 86e1051a39Sopenharmony_ci (void)BIO_flush(bio); 87e1051a39Sopenharmony_ci /* Free up successive BIOs until we hit the old output BIO */ 88e1051a39Sopenharmony_ci do { 89e1051a39Sopenharmony_ci tbio = BIO_pop(bio); 90e1051a39Sopenharmony_ci BIO_free(bio); 91e1051a39Sopenharmony_ci bio = tbio; 92e1051a39Sopenharmony_ci } while (bio != out); 93e1051a39Sopenharmony_ci } 94e1051a39Sopenharmony_ci /* 95e1051a39Sopenharmony_ci * else just write out ASN1 structure which will have all content stored 96e1051a39Sopenharmony_ci * internally 97e1051a39Sopenharmony_ci */ 98e1051a39Sopenharmony_ci else 99e1051a39Sopenharmony_ci ASN1_item_i2d_bio(it, out, val); 100e1051a39Sopenharmony_ci return rv; 101e1051a39Sopenharmony_ci} 102e1051a39Sopenharmony_ci 103e1051a39Sopenharmony_ci/* Base 64 read and write of ASN1 structure */ 104e1051a39Sopenharmony_ci 105e1051a39Sopenharmony_cistatic int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 106e1051a39Sopenharmony_ci const ASN1_ITEM *it) 107e1051a39Sopenharmony_ci{ 108e1051a39Sopenharmony_ci BIO *b64; 109e1051a39Sopenharmony_ci int r; 110e1051a39Sopenharmony_ci b64 = BIO_new(BIO_f_base64()); 111e1051a39Sopenharmony_ci if (b64 == NULL) { 112e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); 113e1051a39Sopenharmony_ci return 0; 114e1051a39Sopenharmony_ci } 115e1051a39Sopenharmony_ci /* 116e1051a39Sopenharmony_ci * prepend the b64 BIO so all data is base64 encoded. 117e1051a39Sopenharmony_ci */ 118e1051a39Sopenharmony_ci out = BIO_push(b64, out); 119e1051a39Sopenharmony_ci r = i2d_ASN1_bio_stream(out, val, in, flags, it); 120e1051a39Sopenharmony_ci (void)BIO_flush(out); 121e1051a39Sopenharmony_ci BIO_pop(out); 122e1051a39Sopenharmony_ci BIO_free(b64); 123e1051a39Sopenharmony_ci return r; 124e1051a39Sopenharmony_ci} 125e1051a39Sopenharmony_ci 126e1051a39Sopenharmony_ci/* Streaming ASN1 PEM write */ 127e1051a39Sopenharmony_ci 128e1051a39Sopenharmony_ciint PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 129e1051a39Sopenharmony_ci const char *hdr, const ASN1_ITEM *it) 130e1051a39Sopenharmony_ci{ 131e1051a39Sopenharmony_ci int r; 132e1051a39Sopenharmony_ci BIO_printf(out, "-----BEGIN %s-----\n", hdr); 133e1051a39Sopenharmony_ci r = B64_write_ASN1(out, val, in, flags, it); 134e1051a39Sopenharmony_ci BIO_printf(out, "-----END %s-----\n", hdr); 135e1051a39Sopenharmony_ci return r; 136e1051a39Sopenharmony_ci} 137e1051a39Sopenharmony_ci 138e1051a39Sopenharmony_cistatic ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it, ASN1_VALUE **x, 139e1051a39Sopenharmony_ci OSSL_LIB_CTX *libctx, const char *propq) 140e1051a39Sopenharmony_ci{ 141e1051a39Sopenharmony_ci BIO *b64; 142e1051a39Sopenharmony_ci ASN1_VALUE *val; 143e1051a39Sopenharmony_ci 144e1051a39Sopenharmony_ci if ((b64 = BIO_new(BIO_f_base64())) == NULL) { 145e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); 146e1051a39Sopenharmony_ci return 0; 147e1051a39Sopenharmony_ci } 148e1051a39Sopenharmony_ci bio = BIO_push(b64, bio); 149e1051a39Sopenharmony_ci val = ASN1_item_d2i_bio_ex(it, bio, x, libctx, propq); 150e1051a39Sopenharmony_ci if (!val) 151e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_DECODE_ERROR); 152e1051a39Sopenharmony_ci (void)BIO_flush(bio); 153e1051a39Sopenharmony_ci BIO_pop(bio); 154e1051a39Sopenharmony_ci BIO_free(b64); 155e1051a39Sopenharmony_ci return val; 156e1051a39Sopenharmony_ci} 157e1051a39Sopenharmony_ci 158e1051a39Sopenharmony_ci/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */ 159e1051a39Sopenharmony_ci 160e1051a39Sopenharmony_cistatic int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs) 161e1051a39Sopenharmony_ci{ 162e1051a39Sopenharmony_ci const EVP_MD *md; 163e1051a39Sopenharmony_ci int i, have_unknown = 0, write_comma, ret = 0, md_nid; 164e1051a39Sopenharmony_ci have_unknown = 0; 165e1051a39Sopenharmony_ci write_comma = 0; 166e1051a39Sopenharmony_ci for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++) { 167e1051a39Sopenharmony_ci if (write_comma) 168e1051a39Sopenharmony_ci BIO_write(out, ",", 1); 169e1051a39Sopenharmony_ci write_comma = 1; 170e1051a39Sopenharmony_ci md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm); 171e1051a39Sopenharmony_ci md = EVP_get_digestbynid(md_nid); 172e1051a39Sopenharmony_ci if (md && md->md_ctrl) { 173e1051a39Sopenharmony_ci int rv; 174e1051a39Sopenharmony_ci char *micstr; 175e1051a39Sopenharmony_ci rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr); 176e1051a39Sopenharmony_ci if (rv > 0) { 177e1051a39Sopenharmony_ci BIO_puts(out, micstr); 178e1051a39Sopenharmony_ci OPENSSL_free(micstr); 179e1051a39Sopenharmony_ci continue; 180e1051a39Sopenharmony_ci } 181e1051a39Sopenharmony_ci if (rv != -2) 182e1051a39Sopenharmony_ci goto err; 183e1051a39Sopenharmony_ci } 184e1051a39Sopenharmony_ci switch (md_nid) { 185e1051a39Sopenharmony_ci case NID_sha1: 186e1051a39Sopenharmony_ci BIO_puts(out, "sha1"); 187e1051a39Sopenharmony_ci break; 188e1051a39Sopenharmony_ci 189e1051a39Sopenharmony_ci case NID_md5: 190e1051a39Sopenharmony_ci BIO_puts(out, "md5"); 191e1051a39Sopenharmony_ci break; 192e1051a39Sopenharmony_ci 193e1051a39Sopenharmony_ci case NID_sha256: 194e1051a39Sopenharmony_ci BIO_puts(out, "sha-256"); 195e1051a39Sopenharmony_ci break; 196e1051a39Sopenharmony_ci 197e1051a39Sopenharmony_ci case NID_sha384: 198e1051a39Sopenharmony_ci BIO_puts(out, "sha-384"); 199e1051a39Sopenharmony_ci break; 200e1051a39Sopenharmony_ci 201e1051a39Sopenharmony_ci case NID_sha512: 202e1051a39Sopenharmony_ci BIO_puts(out, "sha-512"); 203e1051a39Sopenharmony_ci break; 204e1051a39Sopenharmony_ci 205e1051a39Sopenharmony_ci case NID_id_GostR3411_94: 206e1051a39Sopenharmony_ci BIO_puts(out, "gostr3411-94"); 207e1051a39Sopenharmony_ci goto err; 208e1051a39Sopenharmony_ci 209e1051a39Sopenharmony_ci case NID_id_GostR3411_2012_256: 210e1051a39Sopenharmony_ci BIO_puts(out, "gostr3411-2012-256"); 211e1051a39Sopenharmony_ci goto err; 212e1051a39Sopenharmony_ci 213e1051a39Sopenharmony_ci case NID_id_GostR3411_2012_512: 214e1051a39Sopenharmony_ci BIO_puts(out, "gostr3411-2012-512"); 215e1051a39Sopenharmony_ci goto err; 216e1051a39Sopenharmony_ci 217e1051a39Sopenharmony_ci default: 218e1051a39Sopenharmony_ci if (have_unknown) { 219e1051a39Sopenharmony_ci write_comma = 0; 220e1051a39Sopenharmony_ci } else { 221e1051a39Sopenharmony_ci BIO_puts(out, "unknown"); 222e1051a39Sopenharmony_ci have_unknown = 1; 223e1051a39Sopenharmony_ci } 224e1051a39Sopenharmony_ci break; 225e1051a39Sopenharmony_ci 226e1051a39Sopenharmony_ci } 227e1051a39Sopenharmony_ci } 228e1051a39Sopenharmony_ci 229e1051a39Sopenharmony_ci ret = 1; 230e1051a39Sopenharmony_ci err: 231e1051a39Sopenharmony_ci 232e1051a39Sopenharmony_ci return ret; 233e1051a39Sopenharmony_ci 234e1051a39Sopenharmony_ci} 235e1051a39Sopenharmony_ci 236e1051a39Sopenharmony_ci/* SMIME sender */ 237e1051a39Sopenharmony_ci 238e1051a39Sopenharmony_ciint SMIME_write_ASN1_ex(BIO *bio, ASN1_VALUE *val, BIO *data, int flags, 239e1051a39Sopenharmony_ci int ctype_nid, int econt_nid, 240e1051a39Sopenharmony_ci STACK_OF(X509_ALGOR) *mdalgs, const ASN1_ITEM *it, 241e1051a39Sopenharmony_ci OSSL_LIB_CTX *libctx, const char *propq) 242e1051a39Sopenharmony_ci{ 243e1051a39Sopenharmony_ci char bound[33], c; 244e1051a39Sopenharmony_ci int i; 245e1051a39Sopenharmony_ci const char *mime_prefix, *mime_eol, *cname = "smime.p7m"; 246e1051a39Sopenharmony_ci const char *msg_type = NULL; 247e1051a39Sopenharmony_ci 248e1051a39Sopenharmony_ci if (flags & SMIME_OLDMIME) 249e1051a39Sopenharmony_ci mime_prefix = "application/x-pkcs7-"; 250e1051a39Sopenharmony_ci else 251e1051a39Sopenharmony_ci mime_prefix = "application/pkcs7-"; 252e1051a39Sopenharmony_ci 253e1051a39Sopenharmony_ci if (flags & SMIME_CRLFEOL) 254e1051a39Sopenharmony_ci mime_eol = "\r\n"; 255e1051a39Sopenharmony_ci else 256e1051a39Sopenharmony_ci mime_eol = "\n"; 257e1051a39Sopenharmony_ci if ((flags & SMIME_DETACHED) && data) { 258e1051a39Sopenharmony_ci /* We want multipart/signed */ 259e1051a39Sopenharmony_ci /* Generate a random boundary */ 260e1051a39Sopenharmony_ci if (RAND_bytes_ex(libctx, (unsigned char *)bound, 32, 0) <= 0) 261e1051a39Sopenharmony_ci return 0; 262e1051a39Sopenharmony_ci for (i = 0; i < 32; i++) { 263e1051a39Sopenharmony_ci c = bound[i] & 0xf; 264e1051a39Sopenharmony_ci if (c < 10) 265e1051a39Sopenharmony_ci c += '0'; 266e1051a39Sopenharmony_ci else 267e1051a39Sopenharmony_ci c += 'A' - 10; 268e1051a39Sopenharmony_ci bound[i] = c; 269e1051a39Sopenharmony_ci } 270e1051a39Sopenharmony_ci bound[32] = 0; 271e1051a39Sopenharmony_ci BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 272e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Type: multipart/signed;"); 273e1051a39Sopenharmony_ci BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix); 274e1051a39Sopenharmony_ci BIO_puts(bio, " micalg=\""); 275e1051a39Sopenharmony_ci asn1_write_micalg(bio, mdalgs); 276e1051a39Sopenharmony_ci BIO_printf(bio, "\"; boundary=\"----%s\"%s%s", 277e1051a39Sopenharmony_ci bound, mime_eol, mime_eol); 278e1051a39Sopenharmony_ci BIO_printf(bio, "This is an S/MIME signed message%s%s", 279e1051a39Sopenharmony_ci mime_eol, mime_eol); 280e1051a39Sopenharmony_ci /* Now write out the first part */ 281e1051a39Sopenharmony_ci BIO_printf(bio, "------%s%s", bound, mime_eol); 282e1051a39Sopenharmony_ci if (!asn1_output_data(bio, data, val, flags, it)) 283e1051a39Sopenharmony_ci return 0; 284e1051a39Sopenharmony_ci BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol); 285e1051a39Sopenharmony_ci 286e1051a39Sopenharmony_ci /* Headers for signature */ 287e1051a39Sopenharmony_ci 288e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 289e1051a39Sopenharmony_ci BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol); 290e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Transfer-Encoding: base64%s", mime_eol); 291e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Disposition: attachment;"); 292e1051a39Sopenharmony_ci BIO_printf(bio, " filename=\"smime.p7s\"%s%s", mime_eol, mime_eol); 293e1051a39Sopenharmony_ci B64_write_ASN1(bio, val, NULL, 0, it); 294e1051a39Sopenharmony_ci BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound, 295e1051a39Sopenharmony_ci mime_eol, mime_eol); 296e1051a39Sopenharmony_ci return 1; 297e1051a39Sopenharmony_ci } 298e1051a39Sopenharmony_ci 299e1051a39Sopenharmony_ci /* Determine smime-type header */ 300e1051a39Sopenharmony_ci 301e1051a39Sopenharmony_ci if (ctype_nid == NID_pkcs7_enveloped) { 302e1051a39Sopenharmony_ci msg_type = "enveloped-data"; 303e1051a39Sopenharmony_ci } else if (ctype_nid == NID_pkcs7_signed) { 304e1051a39Sopenharmony_ci if (econt_nid == NID_id_smime_ct_receipt) 305e1051a39Sopenharmony_ci msg_type = "signed-receipt"; 306e1051a39Sopenharmony_ci else if (sk_X509_ALGOR_num(mdalgs) >= 0) 307e1051a39Sopenharmony_ci msg_type = "signed-data"; 308e1051a39Sopenharmony_ci else 309e1051a39Sopenharmony_ci msg_type = "certs-only"; 310e1051a39Sopenharmony_ci } else if (ctype_nid == NID_id_smime_ct_compressedData) { 311e1051a39Sopenharmony_ci msg_type = "compressed-data"; 312e1051a39Sopenharmony_ci cname = "smime.p7z"; 313e1051a39Sopenharmony_ci } 314e1051a39Sopenharmony_ci /* MIME headers */ 315e1051a39Sopenharmony_ci BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 316e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Disposition: attachment;"); 317e1051a39Sopenharmony_ci BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol); 318e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Type: %smime;", mime_prefix); 319e1051a39Sopenharmony_ci if (msg_type) 320e1051a39Sopenharmony_ci BIO_printf(bio, " smime-type=%s;", msg_type); 321e1051a39Sopenharmony_ci BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol); 322e1051a39Sopenharmony_ci BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s", 323e1051a39Sopenharmony_ci mime_eol, mime_eol); 324e1051a39Sopenharmony_ci if (!B64_write_ASN1(bio, val, data, flags, it)) 325e1051a39Sopenharmony_ci return 0; 326e1051a39Sopenharmony_ci BIO_printf(bio, "%s", mime_eol); 327e1051a39Sopenharmony_ci return 1; 328e1051a39Sopenharmony_ci} 329e1051a39Sopenharmony_ci 330e1051a39Sopenharmony_ciint SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags, 331e1051a39Sopenharmony_ci int ctype_nid, int econt_nid, 332e1051a39Sopenharmony_ci STACK_OF(X509_ALGOR) *mdalgs, const ASN1_ITEM *it) 333e1051a39Sopenharmony_ci{ 334e1051a39Sopenharmony_ci return SMIME_write_ASN1_ex(bio, val, data, flags, ctype_nid, econt_nid, 335e1051a39Sopenharmony_ci mdalgs, it, NULL, NULL); 336e1051a39Sopenharmony_ci} 337e1051a39Sopenharmony_ci 338e1051a39Sopenharmony_ci/* Handle output of ASN1 data */ 339e1051a39Sopenharmony_ci 340e1051a39Sopenharmony_ci/* cannot constify val because of CMS_dataFinal() */ 341e1051a39Sopenharmony_cistatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 342e1051a39Sopenharmony_ci const ASN1_ITEM *it) 343e1051a39Sopenharmony_ci{ 344e1051a39Sopenharmony_ci BIO *tmpbio; 345e1051a39Sopenharmony_ci const ASN1_AUX *aux = it->funcs; 346e1051a39Sopenharmony_ci ASN1_STREAM_ARG sarg; 347e1051a39Sopenharmony_ci int rv = 1; 348e1051a39Sopenharmony_ci 349e1051a39Sopenharmony_ci /* 350e1051a39Sopenharmony_ci * If data is not detached or resigning then the output BIO is already 351e1051a39Sopenharmony_ci * set up to finalise when it is written through. 352e1051a39Sopenharmony_ci */ 353e1051a39Sopenharmony_ci if (!(flags & SMIME_DETACHED) || (flags & PKCS7_REUSE_DIGEST)) { 354e1051a39Sopenharmony_ci return SMIME_crlf_copy(data, out, flags); 355e1051a39Sopenharmony_ci } 356e1051a39Sopenharmony_ci 357e1051a39Sopenharmony_ci if (!aux || !aux->asn1_cb) { 358e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_STREAMING_NOT_SUPPORTED); 359e1051a39Sopenharmony_ci return 0; 360e1051a39Sopenharmony_ci } 361e1051a39Sopenharmony_ci 362e1051a39Sopenharmony_ci sarg.out = out; 363e1051a39Sopenharmony_ci sarg.ndef_bio = NULL; 364e1051a39Sopenharmony_ci sarg.boundary = NULL; 365e1051a39Sopenharmony_ci 366e1051a39Sopenharmony_ci /* Let ASN1 code prepend any needed BIOs */ 367e1051a39Sopenharmony_ci 368e1051a39Sopenharmony_ci if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0) 369e1051a39Sopenharmony_ci return 0; 370e1051a39Sopenharmony_ci 371e1051a39Sopenharmony_ci /* Copy data across, passing through filter BIOs for processing */ 372e1051a39Sopenharmony_ci if (!SMIME_crlf_copy(data, sarg.ndef_bio, flags)) 373e1051a39Sopenharmony_ci rv = 0; 374e1051a39Sopenharmony_ci 375e1051a39Sopenharmony_ci /* Finalize structure */ 376e1051a39Sopenharmony_ci if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0) 377e1051a39Sopenharmony_ci rv = 0; 378e1051a39Sopenharmony_ci 379e1051a39Sopenharmony_ci /* Now remove any digests prepended to the BIO */ 380e1051a39Sopenharmony_ci 381e1051a39Sopenharmony_ci while (sarg.ndef_bio != out) { 382e1051a39Sopenharmony_ci tmpbio = BIO_pop(sarg.ndef_bio); 383e1051a39Sopenharmony_ci BIO_free(sarg.ndef_bio); 384e1051a39Sopenharmony_ci sarg.ndef_bio = tmpbio; 385e1051a39Sopenharmony_ci } 386e1051a39Sopenharmony_ci 387e1051a39Sopenharmony_ci return rv; 388e1051a39Sopenharmony_ci 389e1051a39Sopenharmony_ci} 390e1051a39Sopenharmony_ci 391e1051a39Sopenharmony_ci/* 392e1051a39Sopenharmony_ci * SMIME reader: handle multipart/signed and opaque signing. in multipart 393e1051a39Sopenharmony_ci * case the content is placed in a memory BIO pointed to by "bcont". In 394e1051a39Sopenharmony_ci * opaque this is set to NULL 395e1051a39Sopenharmony_ci */ 396e1051a39Sopenharmony_ci 397e1051a39Sopenharmony_ciASN1_VALUE *SMIME_read_ASN1_ex(BIO *bio, int flags, BIO **bcont, 398e1051a39Sopenharmony_ci const ASN1_ITEM *it, ASN1_VALUE **x, 399e1051a39Sopenharmony_ci OSSL_LIB_CTX *libctx, const char *propq) 400e1051a39Sopenharmony_ci{ 401e1051a39Sopenharmony_ci BIO *asnin; 402e1051a39Sopenharmony_ci STACK_OF(MIME_HEADER) *headers = NULL; 403e1051a39Sopenharmony_ci STACK_OF(BIO) *parts = NULL; 404e1051a39Sopenharmony_ci MIME_HEADER *hdr; 405e1051a39Sopenharmony_ci MIME_PARAM *prm; 406e1051a39Sopenharmony_ci ASN1_VALUE *val; 407e1051a39Sopenharmony_ci int ret; 408e1051a39Sopenharmony_ci 409e1051a39Sopenharmony_ci if (bcont) 410e1051a39Sopenharmony_ci *bcont = NULL; 411e1051a39Sopenharmony_ci 412e1051a39Sopenharmony_ci if ((headers = mime_parse_hdr(bio)) == NULL) { 413e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR); 414e1051a39Sopenharmony_ci return NULL; 415e1051a39Sopenharmony_ci } 416e1051a39Sopenharmony_ci 417e1051a39Sopenharmony_ci if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 418e1051a39Sopenharmony_ci || hdr->value == NULL) { 419e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 420e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_NO_CONTENT_TYPE); 421e1051a39Sopenharmony_ci return NULL; 422e1051a39Sopenharmony_ci } 423e1051a39Sopenharmony_ci 424e1051a39Sopenharmony_ci /* Handle multipart/signed */ 425e1051a39Sopenharmony_ci 426e1051a39Sopenharmony_ci if (strcmp(hdr->value, "multipart/signed") == 0) { 427e1051a39Sopenharmony_ci /* Split into two parts */ 428e1051a39Sopenharmony_ci prm = mime_param_find(hdr, "boundary"); 429e1051a39Sopenharmony_ci if (prm == NULL || prm->param_value == NULL) { 430e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 431e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY); 432e1051a39Sopenharmony_ci return NULL; 433e1051a39Sopenharmony_ci } 434e1051a39Sopenharmony_ci ret = multi_split(bio, flags, prm->param_value, &parts); 435e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 436e1051a39Sopenharmony_ci if (!ret || (sk_BIO_num(parts) != 2)) { 437e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE); 438e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 439e1051a39Sopenharmony_ci return NULL; 440e1051a39Sopenharmony_ci } 441e1051a39Sopenharmony_ci 442e1051a39Sopenharmony_ci /* Parse the signature piece */ 443e1051a39Sopenharmony_ci asnin = sk_BIO_value(parts, 1); 444e1051a39Sopenharmony_ci 445e1051a39Sopenharmony_ci if ((headers = mime_parse_hdr(asnin)) == NULL) { 446e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR); 447e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 448e1051a39Sopenharmony_ci return NULL; 449e1051a39Sopenharmony_ci } 450e1051a39Sopenharmony_ci 451e1051a39Sopenharmony_ci /* Get content type */ 452e1051a39Sopenharmony_ci 453e1051a39Sopenharmony_ci if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 454e1051a39Sopenharmony_ci || hdr->value == NULL) { 455e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 456e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE); 457e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 458e1051a39Sopenharmony_ci return NULL; 459e1051a39Sopenharmony_ci } 460e1051a39Sopenharmony_ci 461e1051a39Sopenharmony_ci if (strcmp(hdr->value, "application/x-pkcs7-signature") && 462e1051a39Sopenharmony_ci strcmp(hdr->value, "application/pkcs7-signature")) { 463e1051a39Sopenharmony_ci ERR_raise_data(ERR_LIB_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE, 464e1051a39Sopenharmony_ci "type: %s", hdr->value); 465e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 466e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 467e1051a39Sopenharmony_ci return NULL; 468e1051a39Sopenharmony_ci } 469e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 470e1051a39Sopenharmony_ci /* Read in ASN1 */ 471e1051a39Sopenharmony_ci if ((val = b64_read_asn1(asnin, it, x, libctx, propq)) == NULL) { 472e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR); 473e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 474e1051a39Sopenharmony_ci return NULL; 475e1051a39Sopenharmony_ci } 476e1051a39Sopenharmony_ci 477e1051a39Sopenharmony_ci if (bcont) { 478e1051a39Sopenharmony_ci *bcont = sk_BIO_value(parts, 0); 479e1051a39Sopenharmony_ci BIO_free(asnin); 480e1051a39Sopenharmony_ci sk_BIO_free(parts); 481e1051a39Sopenharmony_ci } else { 482e1051a39Sopenharmony_ci sk_BIO_pop_free(parts, BIO_vfree); 483e1051a39Sopenharmony_ci } 484e1051a39Sopenharmony_ci return val; 485e1051a39Sopenharmony_ci } 486e1051a39Sopenharmony_ci 487e1051a39Sopenharmony_ci /* OK, if not multipart/signed try opaque signature */ 488e1051a39Sopenharmony_ci 489e1051a39Sopenharmony_ci if (strcmp(hdr->value, "application/x-pkcs7-mime") && 490e1051a39Sopenharmony_ci strcmp(hdr->value, "application/pkcs7-mime")) { 491e1051a39Sopenharmony_ci ERR_raise_data(ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE, 492e1051a39Sopenharmony_ci "type: %s", hdr->value); 493e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 494e1051a39Sopenharmony_ci return NULL; 495e1051a39Sopenharmony_ci } 496e1051a39Sopenharmony_ci 497e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 498e1051a39Sopenharmony_ci 499e1051a39Sopenharmony_ci if ((val = b64_read_asn1(bio, it, x, libctx, propq)) == NULL) { 500e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_ASN1_PARSE_ERROR); 501e1051a39Sopenharmony_ci return NULL; 502e1051a39Sopenharmony_ci } 503e1051a39Sopenharmony_ci return val; 504e1051a39Sopenharmony_ci} 505e1051a39Sopenharmony_ci 506e1051a39Sopenharmony_ciASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it) 507e1051a39Sopenharmony_ci{ 508e1051a39Sopenharmony_ci return SMIME_read_ASN1_ex(bio, 0, bcont, it, NULL, NULL, NULL); 509e1051a39Sopenharmony_ci} 510e1051a39Sopenharmony_ci 511e1051a39Sopenharmony_ci/* Copy text from one BIO to another making the output CRLF at EOL */ 512e1051a39Sopenharmony_ciint SMIME_crlf_copy(BIO *in, BIO *out, int flags) 513e1051a39Sopenharmony_ci{ 514e1051a39Sopenharmony_ci BIO *bf; 515e1051a39Sopenharmony_ci char eol; 516e1051a39Sopenharmony_ci int len; 517e1051a39Sopenharmony_ci char linebuf[MAX_SMLEN]; 518e1051a39Sopenharmony_ci int ret; 519e1051a39Sopenharmony_ci /* 520e1051a39Sopenharmony_ci * Buffer output so we don't write one line at a time. This is useful 521e1051a39Sopenharmony_ci * when streaming as we don't end up with one OCTET STRING per line. 522e1051a39Sopenharmony_ci */ 523e1051a39Sopenharmony_ci bf = BIO_new(BIO_f_buffer()); 524e1051a39Sopenharmony_ci if (bf == NULL) { 525e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); 526e1051a39Sopenharmony_ci return 0; 527e1051a39Sopenharmony_ci } 528e1051a39Sopenharmony_ci out = BIO_push(bf, out); 529e1051a39Sopenharmony_ci if (flags & SMIME_BINARY) { 530e1051a39Sopenharmony_ci while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 531e1051a39Sopenharmony_ci BIO_write(out, linebuf, len); 532e1051a39Sopenharmony_ci } else { 533e1051a39Sopenharmony_ci int eolcnt = 0; 534e1051a39Sopenharmony_ci if (flags & SMIME_TEXT) 535e1051a39Sopenharmony_ci BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 536e1051a39Sopenharmony_ci while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 537e1051a39Sopenharmony_ci eol = strip_eol(linebuf, &len, flags); 538e1051a39Sopenharmony_ci if (len > 0) { 539e1051a39Sopenharmony_ci /* Not EOF: write out all CRLF */ 540e1051a39Sopenharmony_ci if (flags & SMIME_ASCIICRLF) { 541e1051a39Sopenharmony_ci int i; 542e1051a39Sopenharmony_ci for (i = 0; i < eolcnt; i++) 543e1051a39Sopenharmony_ci BIO_write(out, "\r\n", 2); 544e1051a39Sopenharmony_ci eolcnt = 0; 545e1051a39Sopenharmony_ci } 546e1051a39Sopenharmony_ci BIO_write(out, linebuf, len); 547e1051a39Sopenharmony_ci if (eol) 548e1051a39Sopenharmony_ci BIO_write(out, "\r\n", 2); 549e1051a39Sopenharmony_ci } else if (flags & SMIME_ASCIICRLF) { 550e1051a39Sopenharmony_ci eolcnt++; 551e1051a39Sopenharmony_ci } else if (eol) { 552e1051a39Sopenharmony_ci BIO_write(out, "\r\n", 2); 553e1051a39Sopenharmony_ci } 554e1051a39Sopenharmony_ci } 555e1051a39Sopenharmony_ci } 556e1051a39Sopenharmony_ci ret = BIO_flush(out); 557e1051a39Sopenharmony_ci BIO_pop(out); 558e1051a39Sopenharmony_ci BIO_free(bf); 559e1051a39Sopenharmony_ci if (ret <= 0) 560e1051a39Sopenharmony_ci return 0; 561e1051a39Sopenharmony_ci 562e1051a39Sopenharmony_ci return 1; 563e1051a39Sopenharmony_ci} 564e1051a39Sopenharmony_ci 565e1051a39Sopenharmony_ci/* Strip off headers if they are text/plain */ 566e1051a39Sopenharmony_ciint SMIME_text(BIO *in, BIO *out) 567e1051a39Sopenharmony_ci{ 568e1051a39Sopenharmony_ci char iobuf[4096]; 569e1051a39Sopenharmony_ci int len; 570e1051a39Sopenharmony_ci STACK_OF(MIME_HEADER) *headers; 571e1051a39Sopenharmony_ci MIME_HEADER *hdr; 572e1051a39Sopenharmony_ci 573e1051a39Sopenharmony_ci if ((headers = mime_parse_hdr(in)) == NULL) { 574e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR); 575e1051a39Sopenharmony_ci return 0; 576e1051a39Sopenharmony_ci } 577e1051a39Sopenharmony_ci if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 578e1051a39Sopenharmony_ci || hdr->value == NULL) { 579e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_NO_CONTENT_TYPE); 580e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 581e1051a39Sopenharmony_ci return 0; 582e1051a39Sopenharmony_ci } 583e1051a39Sopenharmony_ci if (strcmp(hdr->value, "text/plain")) { 584e1051a39Sopenharmony_ci ERR_raise_data(ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE, 585e1051a39Sopenharmony_ci "type: %s", hdr->value); 586e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 587e1051a39Sopenharmony_ci return 0; 588e1051a39Sopenharmony_ci } 589e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 590e1051a39Sopenharmony_ci while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 591e1051a39Sopenharmony_ci BIO_write(out, iobuf, len); 592e1051a39Sopenharmony_ci if (len < 0) 593e1051a39Sopenharmony_ci return 0; 594e1051a39Sopenharmony_ci return 1; 595e1051a39Sopenharmony_ci} 596e1051a39Sopenharmony_ci 597e1051a39Sopenharmony_ci/* 598e1051a39Sopenharmony_ci * Split a multipart/XXX message body into component parts: result is 599e1051a39Sopenharmony_ci * canonical parts in a STACK of bios 600e1051a39Sopenharmony_ci */ 601e1051a39Sopenharmony_ci 602e1051a39Sopenharmony_cistatic int multi_split(BIO *bio, int flags, const char *bound, STACK_OF(BIO) **ret) 603e1051a39Sopenharmony_ci{ 604e1051a39Sopenharmony_ci char linebuf[MAX_SMLEN]; 605e1051a39Sopenharmony_ci int len, blen; 606e1051a39Sopenharmony_ci int eol = 0, next_eol = 0; 607e1051a39Sopenharmony_ci BIO *bpart = NULL; 608e1051a39Sopenharmony_ci STACK_OF(BIO) *parts; 609e1051a39Sopenharmony_ci char state, part, first; 610e1051a39Sopenharmony_ci 611e1051a39Sopenharmony_ci blen = strlen(bound); 612e1051a39Sopenharmony_ci part = 0; 613e1051a39Sopenharmony_ci state = 0; 614e1051a39Sopenharmony_ci first = 1; 615e1051a39Sopenharmony_ci parts = sk_BIO_new_null(); 616e1051a39Sopenharmony_ci *ret = parts; 617e1051a39Sopenharmony_ci if (*ret == NULL) 618e1051a39Sopenharmony_ci return 0; 619e1051a39Sopenharmony_ci while ((len = BIO_get_line(bio, linebuf, MAX_SMLEN)) > 0) { 620e1051a39Sopenharmony_ci state = mime_bound_check(linebuf, len, bound, blen); 621e1051a39Sopenharmony_ci if (state == 1) { 622e1051a39Sopenharmony_ci first = 1; 623e1051a39Sopenharmony_ci part++; 624e1051a39Sopenharmony_ci } else if (state == 2) { 625e1051a39Sopenharmony_ci if (!sk_BIO_push(parts, bpart)) { 626e1051a39Sopenharmony_ci BIO_free(bpart); 627e1051a39Sopenharmony_ci return 0; 628e1051a39Sopenharmony_ci } 629e1051a39Sopenharmony_ci return 1; 630e1051a39Sopenharmony_ci } else if (part != 0) { 631e1051a39Sopenharmony_ci /* Strip (possibly CR +) LF from linebuf */ 632e1051a39Sopenharmony_ci next_eol = strip_eol(linebuf, &len, flags); 633e1051a39Sopenharmony_ci if (first) { 634e1051a39Sopenharmony_ci first = 0; 635e1051a39Sopenharmony_ci if (bpart) 636e1051a39Sopenharmony_ci if (!sk_BIO_push(parts, bpart)) { 637e1051a39Sopenharmony_ci BIO_free(bpart); 638e1051a39Sopenharmony_ci return 0; 639e1051a39Sopenharmony_ci } 640e1051a39Sopenharmony_ci bpart = BIO_new(BIO_s_mem()); 641e1051a39Sopenharmony_ci if (bpart == NULL) 642e1051a39Sopenharmony_ci return 0; 643e1051a39Sopenharmony_ci BIO_set_mem_eof_return(bpart, 0); 644e1051a39Sopenharmony_ci } else if (eol) { 645e1051a39Sopenharmony_ci if ( 646e1051a39Sopenharmony_ci#ifndef OPENSSL_NO_CMS 647e1051a39Sopenharmony_ci (flags & CMS_BINARY) == 0 648e1051a39Sopenharmony_ci#else 649e1051a39Sopenharmony_ci 1 650e1051a39Sopenharmony_ci#endif 651e1051a39Sopenharmony_ci || (flags & SMIME_CRLFEOL) != 0) 652e1051a39Sopenharmony_ci BIO_write(bpart, "\r\n", 2); 653e1051a39Sopenharmony_ci else 654e1051a39Sopenharmony_ci BIO_write(bpart, "\n", 1); 655e1051a39Sopenharmony_ci } 656e1051a39Sopenharmony_ci eol = next_eol; 657e1051a39Sopenharmony_ci if (len > 0) 658e1051a39Sopenharmony_ci BIO_write(bpart, linebuf, len); 659e1051a39Sopenharmony_ci } 660e1051a39Sopenharmony_ci } 661e1051a39Sopenharmony_ci BIO_free(bpart); 662e1051a39Sopenharmony_ci return 0; 663e1051a39Sopenharmony_ci} 664e1051a39Sopenharmony_ci 665e1051a39Sopenharmony_ci/* This is the big one: parse MIME header lines up to message body */ 666e1051a39Sopenharmony_ci 667e1051a39Sopenharmony_ci#define MIME_INVALID 0 668e1051a39Sopenharmony_ci#define MIME_START 1 669e1051a39Sopenharmony_ci#define MIME_TYPE 2 670e1051a39Sopenharmony_ci#define MIME_NAME 3 671e1051a39Sopenharmony_ci#define MIME_VALUE 4 672e1051a39Sopenharmony_ci#define MIME_QUOTE 5 673e1051a39Sopenharmony_ci#define MIME_COMMENT 6 674e1051a39Sopenharmony_ci 675e1051a39Sopenharmony_cistatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 676e1051a39Sopenharmony_ci{ 677e1051a39Sopenharmony_ci char *p, *q, c; 678e1051a39Sopenharmony_ci char *ntmp; 679e1051a39Sopenharmony_ci char linebuf[MAX_SMLEN]; 680e1051a39Sopenharmony_ci MIME_HEADER *mhdr = NULL, *new_hdr = NULL; 681e1051a39Sopenharmony_ci STACK_OF(MIME_HEADER) *headers; 682e1051a39Sopenharmony_ci int len, state, save_state = 0; 683e1051a39Sopenharmony_ci 684e1051a39Sopenharmony_ci headers = sk_MIME_HEADER_new(mime_hdr_cmp); 685e1051a39Sopenharmony_ci if (headers == NULL) 686e1051a39Sopenharmony_ci return NULL; 687e1051a39Sopenharmony_ci while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 688e1051a39Sopenharmony_ci /* If whitespace at line start then continuation line */ 689e1051a39Sopenharmony_ci if (mhdr && ossl_isspace(linebuf[0])) 690e1051a39Sopenharmony_ci state = MIME_NAME; 691e1051a39Sopenharmony_ci else 692e1051a39Sopenharmony_ci state = MIME_START; 693e1051a39Sopenharmony_ci ntmp = NULL; 694e1051a39Sopenharmony_ci /* Go through all characters */ 695e1051a39Sopenharmony_ci for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); 696e1051a39Sopenharmony_ci p++) { 697e1051a39Sopenharmony_ci 698e1051a39Sopenharmony_ci /* 699e1051a39Sopenharmony_ci * State machine to handle MIME headers if this looks horrible 700e1051a39Sopenharmony_ci * that's because it *is* 701e1051a39Sopenharmony_ci */ 702e1051a39Sopenharmony_ci 703e1051a39Sopenharmony_ci switch (state) { 704e1051a39Sopenharmony_ci case MIME_START: 705e1051a39Sopenharmony_ci if (c == ':') { 706e1051a39Sopenharmony_ci state = MIME_TYPE; 707e1051a39Sopenharmony_ci *p = 0; 708e1051a39Sopenharmony_ci ntmp = strip_ends(q); 709e1051a39Sopenharmony_ci q = p + 1; 710e1051a39Sopenharmony_ci } 711e1051a39Sopenharmony_ci break; 712e1051a39Sopenharmony_ci 713e1051a39Sopenharmony_ci case MIME_TYPE: 714e1051a39Sopenharmony_ci if (c == ';') { 715e1051a39Sopenharmony_ci mime_debug("Found End Value\n"); 716e1051a39Sopenharmony_ci *p = 0; 717e1051a39Sopenharmony_ci new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 718e1051a39Sopenharmony_ci if (new_hdr == NULL) 719e1051a39Sopenharmony_ci goto err; 720e1051a39Sopenharmony_ci if (!sk_MIME_HEADER_push(headers, new_hdr)) 721e1051a39Sopenharmony_ci goto err; 722e1051a39Sopenharmony_ci mhdr = new_hdr; 723e1051a39Sopenharmony_ci new_hdr = NULL; 724e1051a39Sopenharmony_ci ntmp = NULL; 725e1051a39Sopenharmony_ci q = p + 1; 726e1051a39Sopenharmony_ci state = MIME_NAME; 727e1051a39Sopenharmony_ci } else if (c == '(') { 728e1051a39Sopenharmony_ci save_state = state; 729e1051a39Sopenharmony_ci state = MIME_COMMENT; 730e1051a39Sopenharmony_ci } 731e1051a39Sopenharmony_ci break; 732e1051a39Sopenharmony_ci 733e1051a39Sopenharmony_ci case MIME_COMMENT: 734e1051a39Sopenharmony_ci if (c == ')') { 735e1051a39Sopenharmony_ci state = save_state; 736e1051a39Sopenharmony_ci } 737e1051a39Sopenharmony_ci break; 738e1051a39Sopenharmony_ci 739e1051a39Sopenharmony_ci case MIME_NAME: 740e1051a39Sopenharmony_ci if (c == '=') { 741e1051a39Sopenharmony_ci state = MIME_VALUE; 742e1051a39Sopenharmony_ci *p = 0; 743e1051a39Sopenharmony_ci ntmp = strip_ends(q); 744e1051a39Sopenharmony_ci q = p + 1; 745e1051a39Sopenharmony_ci } 746e1051a39Sopenharmony_ci break; 747e1051a39Sopenharmony_ci 748e1051a39Sopenharmony_ci case MIME_VALUE: 749e1051a39Sopenharmony_ci if (c == ';') { 750e1051a39Sopenharmony_ci state = MIME_NAME; 751e1051a39Sopenharmony_ci *p = 0; 752e1051a39Sopenharmony_ci mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 753e1051a39Sopenharmony_ci ntmp = NULL; 754e1051a39Sopenharmony_ci q = p + 1; 755e1051a39Sopenharmony_ci } else if (c == '"') { 756e1051a39Sopenharmony_ci mime_debug("Found Quote\n"); 757e1051a39Sopenharmony_ci state = MIME_QUOTE; 758e1051a39Sopenharmony_ci } else if (c == '(') { 759e1051a39Sopenharmony_ci save_state = state; 760e1051a39Sopenharmony_ci state = MIME_COMMENT; 761e1051a39Sopenharmony_ci } 762e1051a39Sopenharmony_ci break; 763e1051a39Sopenharmony_ci 764e1051a39Sopenharmony_ci case MIME_QUOTE: 765e1051a39Sopenharmony_ci if (c == '"') { 766e1051a39Sopenharmony_ci mime_debug("Found Match Quote\n"); 767e1051a39Sopenharmony_ci state = MIME_VALUE; 768e1051a39Sopenharmony_ci } 769e1051a39Sopenharmony_ci break; 770e1051a39Sopenharmony_ci } 771e1051a39Sopenharmony_ci } 772e1051a39Sopenharmony_ci 773e1051a39Sopenharmony_ci if (state == MIME_TYPE) { 774e1051a39Sopenharmony_ci new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 775e1051a39Sopenharmony_ci if (new_hdr == NULL) 776e1051a39Sopenharmony_ci goto err; 777e1051a39Sopenharmony_ci if (!sk_MIME_HEADER_push(headers, new_hdr)) 778e1051a39Sopenharmony_ci goto err; 779e1051a39Sopenharmony_ci mhdr = new_hdr; 780e1051a39Sopenharmony_ci new_hdr = NULL; 781e1051a39Sopenharmony_ci } else if (state == MIME_VALUE) { 782e1051a39Sopenharmony_ci mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 783e1051a39Sopenharmony_ci } 784e1051a39Sopenharmony_ci if (p == linebuf) 785e1051a39Sopenharmony_ci break; /* Blank line means end of headers */ 786e1051a39Sopenharmony_ci } 787e1051a39Sopenharmony_ci 788e1051a39Sopenharmony_ci return headers; 789e1051a39Sopenharmony_ci 790e1051a39Sopenharmony_ci err: 791e1051a39Sopenharmony_ci mime_hdr_free(new_hdr); 792e1051a39Sopenharmony_ci sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 793e1051a39Sopenharmony_ci return NULL; 794e1051a39Sopenharmony_ci} 795e1051a39Sopenharmony_ci 796e1051a39Sopenharmony_cistatic char *strip_ends(char *name) 797e1051a39Sopenharmony_ci{ 798e1051a39Sopenharmony_ci return strip_end(strip_start(name)); 799e1051a39Sopenharmony_ci} 800e1051a39Sopenharmony_ci 801e1051a39Sopenharmony_ci/* Strip a parameter of whitespace from start of param */ 802e1051a39Sopenharmony_cistatic char *strip_start(char *name) 803e1051a39Sopenharmony_ci{ 804e1051a39Sopenharmony_ci char *p, c; 805e1051a39Sopenharmony_ci /* Look for first non whitespace or quote */ 806e1051a39Sopenharmony_ci for (p = name; (c = *p); p++) { 807e1051a39Sopenharmony_ci if (c == '"') { 808e1051a39Sopenharmony_ci /* Next char is start of string if non null */ 809e1051a39Sopenharmony_ci if (p[1]) 810e1051a39Sopenharmony_ci return p + 1; 811e1051a39Sopenharmony_ci /* Else null string */ 812e1051a39Sopenharmony_ci return NULL; 813e1051a39Sopenharmony_ci } 814e1051a39Sopenharmony_ci if (!ossl_isspace(c)) 815e1051a39Sopenharmony_ci return p; 816e1051a39Sopenharmony_ci } 817e1051a39Sopenharmony_ci return NULL; 818e1051a39Sopenharmony_ci} 819e1051a39Sopenharmony_ci 820e1051a39Sopenharmony_ci/* As above but strip from end of string : maybe should handle brackets? */ 821e1051a39Sopenharmony_cistatic char *strip_end(char *name) 822e1051a39Sopenharmony_ci{ 823e1051a39Sopenharmony_ci char *p, c; 824e1051a39Sopenharmony_ci if (!name) 825e1051a39Sopenharmony_ci return NULL; 826e1051a39Sopenharmony_ci /* Look for first non whitespace or quote */ 827e1051a39Sopenharmony_ci for (p = name + strlen(name) - 1; p >= name; p--) { 828e1051a39Sopenharmony_ci c = *p; 829e1051a39Sopenharmony_ci if (c == '"') { 830e1051a39Sopenharmony_ci if (p - 1 == name) 831e1051a39Sopenharmony_ci return NULL; 832e1051a39Sopenharmony_ci *p = 0; 833e1051a39Sopenharmony_ci return name; 834e1051a39Sopenharmony_ci } 835e1051a39Sopenharmony_ci if (ossl_isspace(c)) 836e1051a39Sopenharmony_ci *p = 0; 837e1051a39Sopenharmony_ci else 838e1051a39Sopenharmony_ci return name; 839e1051a39Sopenharmony_ci } 840e1051a39Sopenharmony_ci return NULL; 841e1051a39Sopenharmony_ci} 842e1051a39Sopenharmony_ci 843e1051a39Sopenharmony_cistatic MIME_HEADER *mime_hdr_new(const char *name, const char *value) 844e1051a39Sopenharmony_ci{ 845e1051a39Sopenharmony_ci MIME_HEADER *mhdr = NULL; 846e1051a39Sopenharmony_ci char *tmpname = NULL, *tmpval = NULL, *p; 847e1051a39Sopenharmony_ci 848e1051a39Sopenharmony_ci if (name) { 849e1051a39Sopenharmony_ci if ((tmpname = OPENSSL_strdup(name)) == NULL) 850e1051a39Sopenharmony_ci return NULL; 851e1051a39Sopenharmony_ci for (p = tmpname; *p; p++) 852e1051a39Sopenharmony_ci *p = ossl_tolower(*p); 853e1051a39Sopenharmony_ci } 854e1051a39Sopenharmony_ci if (value) { 855e1051a39Sopenharmony_ci if ((tmpval = OPENSSL_strdup(value)) == NULL) 856e1051a39Sopenharmony_ci goto err; 857e1051a39Sopenharmony_ci for (p = tmpval; *p; p++) 858e1051a39Sopenharmony_ci *p = ossl_tolower(*p); 859e1051a39Sopenharmony_ci } 860e1051a39Sopenharmony_ci mhdr = OPENSSL_malloc(sizeof(*mhdr)); 861e1051a39Sopenharmony_ci if (mhdr == NULL) 862e1051a39Sopenharmony_ci goto err; 863e1051a39Sopenharmony_ci mhdr->name = tmpname; 864e1051a39Sopenharmony_ci mhdr->value = tmpval; 865e1051a39Sopenharmony_ci if ((mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)) == NULL) 866e1051a39Sopenharmony_ci goto err; 867e1051a39Sopenharmony_ci return mhdr; 868e1051a39Sopenharmony_ci 869e1051a39Sopenharmony_ci err: 870e1051a39Sopenharmony_ci OPENSSL_free(tmpname); 871e1051a39Sopenharmony_ci OPENSSL_free(tmpval); 872e1051a39Sopenharmony_ci OPENSSL_free(mhdr); 873e1051a39Sopenharmony_ci return NULL; 874e1051a39Sopenharmony_ci} 875e1051a39Sopenharmony_ci 876e1051a39Sopenharmony_cistatic int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value) 877e1051a39Sopenharmony_ci{ 878e1051a39Sopenharmony_ci char *tmpname = NULL, *tmpval = NULL, *p; 879e1051a39Sopenharmony_ci MIME_PARAM *mparam = NULL; 880e1051a39Sopenharmony_ci 881e1051a39Sopenharmony_ci if (name) { 882e1051a39Sopenharmony_ci tmpname = OPENSSL_strdup(name); 883e1051a39Sopenharmony_ci if (!tmpname) 884e1051a39Sopenharmony_ci goto err; 885e1051a39Sopenharmony_ci for (p = tmpname; *p; p++) 886e1051a39Sopenharmony_ci *p = ossl_tolower(*p); 887e1051a39Sopenharmony_ci } 888e1051a39Sopenharmony_ci if (value) { 889e1051a39Sopenharmony_ci tmpval = OPENSSL_strdup(value); 890e1051a39Sopenharmony_ci if (!tmpval) 891e1051a39Sopenharmony_ci goto err; 892e1051a39Sopenharmony_ci } 893e1051a39Sopenharmony_ci /* Parameter values are case sensitive so leave as is */ 894e1051a39Sopenharmony_ci mparam = OPENSSL_malloc(sizeof(*mparam)); 895e1051a39Sopenharmony_ci if (mparam == NULL) 896e1051a39Sopenharmony_ci goto err; 897e1051a39Sopenharmony_ci mparam->param_name = tmpname; 898e1051a39Sopenharmony_ci mparam->param_value = tmpval; 899e1051a39Sopenharmony_ci if (!sk_MIME_PARAM_push(mhdr->params, mparam)) 900e1051a39Sopenharmony_ci goto err; 901e1051a39Sopenharmony_ci return 1; 902e1051a39Sopenharmony_ci err: 903e1051a39Sopenharmony_ci OPENSSL_free(tmpname); 904e1051a39Sopenharmony_ci OPENSSL_free(tmpval); 905e1051a39Sopenharmony_ci OPENSSL_free(mparam); 906e1051a39Sopenharmony_ci return 0; 907e1051a39Sopenharmony_ci} 908e1051a39Sopenharmony_ci 909e1051a39Sopenharmony_cistatic int mime_hdr_cmp(const MIME_HEADER *const *a, 910e1051a39Sopenharmony_ci const MIME_HEADER *const *b) 911e1051a39Sopenharmony_ci{ 912e1051a39Sopenharmony_ci if ((*a)->name == NULL || (*b)->name == NULL) 913e1051a39Sopenharmony_ci return ((*a)->name != NULL) - ((*b)->name != NULL); 914e1051a39Sopenharmony_ci 915e1051a39Sopenharmony_ci return strcmp((*a)->name, (*b)->name); 916e1051a39Sopenharmony_ci} 917e1051a39Sopenharmony_ci 918e1051a39Sopenharmony_cistatic int mime_param_cmp(const MIME_PARAM *const *a, 919e1051a39Sopenharmony_ci const MIME_PARAM *const *b) 920e1051a39Sopenharmony_ci{ 921e1051a39Sopenharmony_ci if ((*a)->param_name == NULL || (*b)->param_name == NULL) 922e1051a39Sopenharmony_ci return ((*a)->param_name != NULL) - ((*b)->param_name != NULL); 923e1051a39Sopenharmony_ci return strcmp((*a)->param_name, (*b)->param_name); 924e1051a39Sopenharmony_ci} 925e1051a39Sopenharmony_ci 926e1051a39Sopenharmony_ci/* Find a header with a given name (if possible) */ 927e1051a39Sopenharmony_ci 928e1051a39Sopenharmony_cistatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name) 929e1051a39Sopenharmony_ci{ 930e1051a39Sopenharmony_ci MIME_HEADER htmp; 931e1051a39Sopenharmony_ci int idx; 932e1051a39Sopenharmony_ci 933e1051a39Sopenharmony_ci htmp.name = (char *)name; 934e1051a39Sopenharmony_ci htmp.value = NULL; 935e1051a39Sopenharmony_ci htmp.params = NULL; 936e1051a39Sopenharmony_ci 937e1051a39Sopenharmony_ci idx = sk_MIME_HEADER_find(hdrs, &htmp); 938e1051a39Sopenharmony_ci return sk_MIME_HEADER_value(hdrs, idx); 939e1051a39Sopenharmony_ci} 940e1051a39Sopenharmony_ci 941e1051a39Sopenharmony_cistatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name) 942e1051a39Sopenharmony_ci{ 943e1051a39Sopenharmony_ci MIME_PARAM param; 944e1051a39Sopenharmony_ci int idx; 945e1051a39Sopenharmony_ci 946e1051a39Sopenharmony_ci param.param_name = (char *)name; 947e1051a39Sopenharmony_ci param.param_value = NULL; 948e1051a39Sopenharmony_ci idx = sk_MIME_PARAM_find(hdr->params, ¶m); 949e1051a39Sopenharmony_ci return sk_MIME_PARAM_value(hdr->params, idx); 950e1051a39Sopenharmony_ci} 951e1051a39Sopenharmony_ci 952e1051a39Sopenharmony_cistatic void mime_hdr_free(MIME_HEADER *hdr) 953e1051a39Sopenharmony_ci{ 954e1051a39Sopenharmony_ci if (hdr == NULL) 955e1051a39Sopenharmony_ci return; 956e1051a39Sopenharmony_ci OPENSSL_free(hdr->name); 957e1051a39Sopenharmony_ci OPENSSL_free(hdr->value); 958e1051a39Sopenharmony_ci if (hdr->params) 959e1051a39Sopenharmony_ci sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 960e1051a39Sopenharmony_ci OPENSSL_free(hdr); 961e1051a39Sopenharmony_ci} 962e1051a39Sopenharmony_ci 963e1051a39Sopenharmony_cistatic void mime_param_free(MIME_PARAM *param) 964e1051a39Sopenharmony_ci{ 965e1051a39Sopenharmony_ci OPENSSL_free(param->param_name); 966e1051a39Sopenharmony_ci OPENSSL_free(param->param_value); 967e1051a39Sopenharmony_ci OPENSSL_free(param); 968e1051a39Sopenharmony_ci} 969e1051a39Sopenharmony_ci 970e1051a39Sopenharmony_ci/*- 971e1051a39Sopenharmony_ci * Check for a multipart boundary. Returns: 972e1051a39Sopenharmony_ci * 0 : no boundary 973e1051a39Sopenharmony_ci * 1 : part boundary 974e1051a39Sopenharmony_ci * 2 : final boundary 975e1051a39Sopenharmony_ci */ 976e1051a39Sopenharmony_cistatic int mime_bound_check(char *line, int linelen, const char *bound, int blen) 977e1051a39Sopenharmony_ci{ 978e1051a39Sopenharmony_ci if (linelen == -1) 979e1051a39Sopenharmony_ci linelen = strlen(line); 980e1051a39Sopenharmony_ci if (blen == -1) 981e1051a39Sopenharmony_ci blen = strlen(bound); 982e1051a39Sopenharmony_ci /* Quickly eliminate if line length too short */ 983e1051a39Sopenharmony_ci if (blen + 2 > linelen) 984e1051a39Sopenharmony_ci return 0; 985e1051a39Sopenharmony_ci /* Check for part boundary */ 986e1051a39Sopenharmony_ci if ((strncmp(line, "--", 2) == 0) 987e1051a39Sopenharmony_ci && strncmp(line + 2, bound, blen) == 0) { 988e1051a39Sopenharmony_ci if (strncmp(line + blen + 2, "--", 2) == 0) 989e1051a39Sopenharmony_ci return 2; 990e1051a39Sopenharmony_ci else 991e1051a39Sopenharmony_ci return 1; 992e1051a39Sopenharmony_ci } 993e1051a39Sopenharmony_ci return 0; 994e1051a39Sopenharmony_ci} 995e1051a39Sopenharmony_ci 996e1051a39Sopenharmony_cistatic int strip_eol(char *linebuf, int *plen, int flags) 997e1051a39Sopenharmony_ci{ 998e1051a39Sopenharmony_ci int len = *plen; 999e1051a39Sopenharmony_ci char *p, c; 1000e1051a39Sopenharmony_ci int is_eol = 0; 1001e1051a39Sopenharmony_ci 1002e1051a39Sopenharmony_ci#ifndef OPENSSL_NO_CMS 1003e1051a39Sopenharmony_ci if ((flags & CMS_BINARY) != 0) { 1004e1051a39Sopenharmony_ci if (len <= 0 || linebuf[len - 1] != '\n') 1005e1051a39Sopenharmony_ci return 0; 1006e1051a39Sopenharmony_ci if ((flags & SMIME_CRLFEOL) != 0) { 1007e1051a39Sopenharmony_ci if (len <= 1 || linebuf[len - 2] != '\r') 1008e1051a39Sopenharmony_ci return 0; 1009e1051a39Sopenharmony_ci len--; 1010e1051a39Sopenharmony_ci } 1011e1051a39Sopenharmony_ci len--; 1012e1051a39Sopenharmony_ci *plen = len; 1013e1051a39Sopenharmony_ci return 1; 1014e1051a39Sopenharmony_ci } 1015e1051a39Sopenharmony_ci#endif 1016e1051a39Sopenharmony_ci 1017e1051a39Sopenharmony_ci for (p = linebuf + len - 1; len > 0; len--, p--) { 1018e1051a39Sopenharmony_ci c = *p; 1019e1051a39Sopenharmony_ci if (c == '\n') { 1020e1051a39Sopenharmony_ci is_eol = 1; 1021e1051a39Sopenharmony_ci } else if (is_eol && (flags & SMIME_ASCIICRLF) != 0 && c == 32) { 1022e1051a39Sopenharmony_ci /* Strip trailing space on a line; 32 == ASCII for ' ' */ 1023e1051a39Sopenharmony_ci continue; 1024e1051a39Sopenharmony_ci } else if (c != '\r') { 1025e1051a39Sopenharmony_ci break; 1026e1051a39Sopenharmony_ci } 1027e1051a39Sopenharmony_ci } 1028e1051a39Sopenharmony_ci *plen = len; 1029e1051a39Sopenharmony_ci return is_eol; 1030e1051a39Sopenharmony_ci} 1031