1*c5925a46Stb /* $OpenBSD: asn_mime.c,v 1.35 2025/01/17 05:02:18 tb Exp $ */ 25650a0e1Sdjm /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 35650a0e1Sdjm * project. 45650a0e1Sdjm */ 55650a0e1Sdjm /* ==================================================================== 65650a0e1Sdjm * Copyright (c) 1999-2008 The OpenSSL Project. All rights reserved. 75650a0e1Sdjm * 85650a0e1Sdjm * Redistribution and use in source and binary forms, with or without 95650a0e1Sdjm * modification, are permitted provided that the following conditions 105650a0e1Sdjm * are met: 115650a0e1Sdjm * 125650a0e1Sdjm * 1. Redistributions of source code must retain the above copyright 135650a0e1Sdjm * notice, this list of conditions and the following disclaimer. 145650a0e1Sdjm * 155650a0e1Sdjm * 2. Redistributions in binary form must reproduce the above copyright 165650a0e1Sdjm * notice, this list of conditions and the following disclaimer in 175650a0e1Sdjm * the documentation and/or other materials provided with the 185650a0e1Sdjm * distribution. 195650a0e1Sdjm * 205650a0e1Sdjm * 3. All advertising materials mentioning features or use of this 215650a0e1Sdjm * software must display the following acknowledgment: 225650a0e1Sdjm * "This product includes software developed by the OpenSSL Project 235650a0e1Sdjm * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 245650a0e1Sdjm * 255650a0e1Sdjm * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 265650a0e1Sdjm * endorse or promote products derived from this software without 275650a0e1Sdjm * prior written permission. For written permission, please contact 285650a0e1Sdjm * licensing@OpenSSL.org. 295650a0e1Sdjm * 305650a0e1Sdjm * 5. Products derived from this software may not be called "OpenSSL" 315650a0e1Sdjm * nor may "OpenSSL" appear in their names without prior written 325650a0e1Sdjm * permission of the OpenSSL Project. 335650a0e1Sdjm * 345650a0e1Sdjm * 6. Redistributions of any form whatsoever must retain the following 355650a0e1Sdjm * acknowledgment: 365650a0e1Sdjm * "This product includes software developed by the OpenSSL Project 375650a0e1Sdjm * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 385650a0e1Sdjm * 395650a0e1Sdjm * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 405650a0e1Sdjm * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 415650a0e1Sdjm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 425650a0e1Sdjm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 435650a0e1Sdjm * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 445650a0e1Sdjm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 455650a0e1Sdjm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 465650a0e1Sdjm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 475650a0e1Sdjm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 485650a0e1Sdjm * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 495650a0e1Sdjm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 505650a0e1Sdjm * OF THE POSSIBILITY OF SUCH DAMAGE. 515650a0e1Sdjm * ==================================================================== 525650a0e1Sdjm * 535650a0e1Sdjm */ 545650a0e1Sdjm 555650a0e1Sdjm #include <ctype.h> 56a8913c44Sjsing #include <stdio.h> 57ef624301Sjsing #include <stdlib.h> 58a8913c44Sjsing #include <string.h> 59a8913c44Sjsing 605650a0e1Sdjm #include <openssl/asn1.h> 615650a0e1Sdjm #include <openssl/asn1t.h> 62b6ab114eSjsing #include <openssl/err.h> 63b6ab114eSjsing #include <openssl/x509.h> 64b6ab114eSjsing 65c9675a23Stb #include "asn1_local.h" 66c9675a23Stb #include "evp_local.h" 675650a0e1Sdjm 685650a0e1Sdjm /* Generalised MIME like utilities for streaming ASN1. Although many 695650a0e1Sdjm * have a PKCS7/CMS like flavour others are more general purpose. 705650a0e1Sdjm */ 715650a0e1Sdjm 725650a0e1Sdjm /* MIME format structures 735650a0e1Sdjm * Note that all are translated to lower case apart from 745650a0e1Sdjm * parameter values. Quotes are stripped off 755650a0e1Sdjm */ 765650a0e1Sdjm 775650a0e1Sdjm typedef struct { 785650a0e1Sdjm char *param_name; /* Param name e.g. "micalg" */ 795650a0e1Sdjm char *param_value; /* Param value e.g. "sha1" */ 805650a0e1Sdjm } MIME_PARAM; 815650a0e1Sdjm 825650a0e1Sdjm DECLARE_STACK_OF(MIME_PARAM) 835650a0e1Sdjm 845650a0e1Sdjm typedef struct { 855650a0e1Sdjm char *name; /* Name of line e.g. "content-type" */ 865650a0e1Sdjm char *value; /* Value of line e.g. "text/plain" */ 875650a0e1Sdjm STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */ 885650a0e1Sdjm } MIME_HEADER; 895650a0e1Sdjm 905650a0e1Sdjm DECLARE_STACK_OF(MIME_HEADER) 915650a0e1Sdjm 920a5d6edeSdjm static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 930a5d6edeSdjm const ASN1_ITEM *it); 945650a0e1Sdjm static char * strip_ends(char *name); 955650a0e1Sdjm static char * strip_start(char *name); 965650a0e1Sdjm static char * strip_end(char *name); 975650a0e1Sdjm static MIME_HEADER *mime_hdr_new(char *name, char *value); 985650a0e1Sdjm static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value); 995650a0e1Sdjm static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); 1005650a0e1Sdjm static int mime_hdr_cmp(const MIME_HEADER * const *a, 1015650a0e1Sdjm const MIME_HEADER * const *b); 1025650a0e1Sdjm static int mime_param_cmp(const MIME_PARAM * const *a, 1035650a0e1Sdjm const MIME_PARAM * const *b); 1045650a0e1Sdjm static void mime_param_free(MIME_PARAM *param); 1055650a0e1Sdjm static int mime_bound_check(char *line, int linelen, char *bound, int blen); 1065650a0e1Sdjm static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret); 1075650a0e1Sdjm static int strip_eol(char *linebuf, int *plen); 1085650a0e1Sdjm static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name); 1095650a0e1Sdjm static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name); 1105650a0e1Sdjm static void mime_hdr_free(MIME_HEADER *hdr); 1115650a0e1Sdjm 1125650a0e1Sdjm #define MAX_SMLEN 1024 1135650a0e1Sdjm 1140a5d6edeSdjm /* Output an ASN1 structure in BER format streaming if necessary */ 1150a5d6edeSdjm 116f309b230Sjsing int 117f309b230Sjsing i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 1180a5d6edeSdjm const ASN1_ITEM *it) 1190a5d6edeSdjm { 1200a5d6edeSdjm BIO *bio, *tbio; 121f312c1a5Stb int ret; 122f312c1a5Stb 123f312c1a5Stb /* Without streaming, write out the ASN.1 structure's content. */ 124f312c1a5Stb if ((flags & SMIME_STREAM) == 0) 125f312c1a5Stb return ASN1_item_i2d_bio(it, out, val); 126f312c1a5Stb 127f312c1a5Stb /* If streaming, create a stream BIO and copy all content through it. */ 128f312c1a5Stb if ((bio = BIO_new_NDEF(out, val, it)) == NULL) { 1295067ae9fSbeck ASN1error(ERR_R_MALLOC_FAILURE); 1300a5d6edeSdjm return 0; 1310a5d6edeSdjm } 132f312c1a5Stb 133f312c1a5Stb ret = SMIME_crlf_copy(in, bio, flags); 1340a5d6edeSdjm (void)BIO_flush(bio); 135f312c1a5Stb 136f312c1a5Stb /* Free up successive BIOs until we hit the old output BIO. */ 137cc777fd4Stedu do { 1380a5d6edeSdjm tbio = BIO_pop(bio); 1390a5d6edeSdjm BIO_free(bio); 1400a5d6edeSdjm bio = tbio; 1410a5d6edeSdjm } while (bio != out); 142f312c1a5Stb 143f312c1a5Stb return ret; 1440a5d6edeSdjm } 1450a5d6edeSdjm 1465650a0e1Sdjm /* Base 64 read and write of ASN1 structure */ 1475650a0e1Sdjm 148f309b230Sjsing static int 149f309b230Sjsing B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 1505650a0e1Sdjm const ASN1_ITEM *it) 1515650a0e1Sdjm { 1525650a0e1Sdjm BIO *b64; 1535650a0e1Sdjm int r; 154f309b230Sjsing 1555650a0e1Sdjm b64 = BIO_new(BIO_f_base64()); 156cc777fd4Stedu if (!b64) { 1575067ae9fSbeck ASN1error(ERR_R_MALLOC_FAILURE); 1585650a0e1Sdjm return 0; 1595650a0e1Sdjm } 1605650a0e1Sdjm /* prepend the b64 BIO so all data is base64 encoded. 1615650a0e1Sdjm */ 1625650a0e1Sdjm out = BIO_push(b64, out); 1630a5d6edeSdjm r = i2d_ASN1_bio_stream(out, val, in, flags, it); 1645650a0e1Sdjm (void)BIO_flush(out); 1655650a0e1Sdjm BIO_pop(out); 1665650a0e1Sdjm BIO_free(b64); 1675650a0e1Sdjm return r; 1685650a0e1Sdjm } 1695650a0e1Sdjm 1700a5d6edeSdjm /* Streaming ASN1 PEM write */ 1710a5d6edeSdjm 172f309b230Sjsing int 173f309b230Sjsing PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 174f309b230Sjsing const char *hdr, const ASN1_ITEM *it) 1750a5d6edeSdjm { 1760a5d6edeSdjm int r; 177f309b230Sjsing 1780a5d6edeSdjm BIO_printf(out, "-----BEGIN %s-----\n", hdr); 1790a5d6edeSdjm r = B64_write_ASN1(out, val, in, flags, it); 1800a5d6edeSdjm BIO_printf(out, "-----END %s-----\n", hdr); 1810a5d6edeSdjm return r; 1820a5d6edeSdjm } 1830a5d6edeSdjm 184f309b230Sjsing static ASN1_VALUE * 185f309b230Sjsing b64_read_asn1(BIO *bio, const ASN1_ITEM *it) 1865650a0e1Sdjm { 1875650a0e1Sdjm BIO *b64; 1885650a0e1Sdjm ASN1_VALUE *val; 1895650a0e1Sdjm if (!(b64 = BIO_new(BIO_f_base64()))) { 1905067ae9fSbeck ASN1error(ERR_R_MALLOC_FAILURE); 1915650a0e1Sdjm return 0; 1925650a0e1Sdjm } 1935650a0e1Sdjm bio = BIO_push(b64, bio); 1945650a0e1Sdjm val = ASN1_item_d2i_bio(it, bio, NULL); 1955650a0e1Sdjm if (!val) 1965067ae9fSbeck ASN1error(ASN1_R_DECODE_ERROR); 1975650a0e1Sdjm (void)BIO_flush(bio); 1985650a0e1Sdjm bio = BIO_pop(bio); 1995650a0e1Sdjm BIO_free(b64); 2005650a0e1Sdjm return val; 2015650a0e1Sdjm } 2025650a0e1Sdjm 2035650a0e1Sdjm /* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */ 2045650a0e1Sdjm 205f309b230Sjsing static int 206f309b230Sjsing asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs) 2075650a0e1Sdjm { 2080a5d6edeSdjm const EVP_MD *md; 2090a5d6edeSdjm int i, have_unknown = 0, write_comma, ret = 0, md_nid; 210f309b230Sjsing 2115650a0e1Sdjm have_unknown = 0; 2125650a0e1Sdjm write_comma = 0; 213cc777fd4Stedu for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++) { 2145650a0e1Sdjm if (write_comma) 2155650a0e1Sdjm BIO_write(out, ",", 1); 2165650a0e1Sdjm write_comma = 1; 2175650a0e1Sdjm md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm); 2180a5d6edeSdjm md = EVP_get_digestbynid(md_nid); 219cc777fd4Stedu if (md && md->md_ctrl) { 2200a5d6edeSdjm int rv; 2210a5d6edeSdjm char *micstr; 2220a5d6edeSdjm rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr); 223cc777fd4Stedu if (rv > 0) { 2240a5d6edeSdjm BIO_puts(out, micstr); 2256f3a6cb1Sbeck free(micstr); 2260a5d6edeSdjm continue; 2270a5d6edeSdjm } 2280a5d6edeSdjm if (rv != -2) 2290a5d6edeSdjm goto err; 2300a5d6edeSdjm } 231cc777fd4Stedu switch (md_nid) { 2325650a0e1Sdjm case NID_sha1: 2335650a0e1Sdjm BIO_puts(out, "sha1"); 2345650a0e1Sdjm break; 2355650a0e1Sdjm 2365650a0e1Sdjm case NID_md5: 2375650a0e1Sdjm BIO_puts(out, "md5"); 2385650a0e1Sdjm break; 2395650a0e1Sdjm 2405650a0e1Sdjm case NID_sha256: 2415650a0e1Sdjm BIO_puts(out, "sha-256"); 2425650a0e1Sdjm break; 2435650a0e1Sdjm 2445650a0e1Sdjm case NID_sha384: 2455650a0e1Sdjm BIO_puts(out, "sha-384"); 2465650a0e1Sdjm break; 2475650a0e1Sdjm 2485650a0e1Sdjm case NID_sha512: 2495650a0e1Sdjm BIO_puts(out, "sha-512"); 2505650a0e1Sdjm break; 2515650a0e1Sdjm 2520a5d6edeSdjm case NID_id_GostR3411_94: 2530a5d6edeSdjm BIO_puts(out, "gostr3411-94"); 2540a5d6edeSdjm goto err; 2550a5d6edeSdjm break; 2560a5d6edeSdjm 2575650a0e1Sdjm default: 2585650a0e1Sdjm if (have_unknown) 2595650a0e1Sdjm write_comma = 0; 260cc777fd4Stedu else { 2615650a0e1Sdjm BIO_puts(out, "unknown"); 2625650a0e1Sdjm have_unknown = 1; 2635650a0e1Sdjm } 2645650a0e1Sdjm break; 2655650a0e1Sdjm 2665650a0e1Sdjm } 2675650a0e1Sdjm } 2685650a0e1Sdjm 2690a5d6edeSdjm ret = 1; 270f309b230Sjsing 2710a5d6edeSdjm err: 2720a5d6edeSdjm return ret; 2735650a0e1Sdjm } 2745650a0e1Sdjm 2755650a0e1Sdjm /* SMIME sender */ 2765650a0e1Sdjm 277f309b230Sjsing int 278f309b230Sjsing SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags, 279f309b230Sjsing int ctype_nid, int econt_nid, STACK_OF(X509_ALGOR) *mdalgs, 2805650a0e1Sdjm const ASN1_ITEM *it) 2815650a0e1Sdjm { 2825650a0e1Sdjm char bound[33], c; 2835650a0e1Sdjm int i; 2845650a0e1Sdjm const char *mime_prefix, *mime_eol, *cname = "smime.p7m"; 2855650a0e1Sdjm const char *msg_type = NULL; 286f309b230Sjsing 2875650a0e1Sdjm if (flags & SMIME_OLDMIME) 2885650a0e1Sdjm mime_prefix = "application/x-pkcs7-"; 2895650a0e1Sdjm else 2905650a0e1Sdjm mime_prefix = "application/pkcs7-"; 2915650a0e1Sdjm 2925650a0e1Sdjm if (flags & SMIME_CRLFEOL) 2935650a0e1Sdjm mime_eol = "\r\n"; 2945650a0e1Sdjm else 2955650a0e1Sdjm mime_eol = "\n"; 2965650a0e1Sdjm if ((flags & SMIME_DETACHED) && data) { 2975650a0e1Sdjm /* We want multipart/signed */ 2985650a0e1Sdjm /* Generate a random boundary */ 299ef624301Sjsing arc4random_buf(bound, 32); 3005650a0e1Sdjm for (i = 0; i < 32; i++) { 3015650a0e1Sdjm c = bound[i] & 0xf; 302f309b230Sjsing if (c < 10) 303f309b230Sjsing c += '0'; 304f309b230Sjsing else 305f309b230Sjsing c += 'A' - 10; 3065650a0e1Sdjm bound[i] = c; 3075650a0e1Sdjm } 3085650a0e1Sdjm bound[32] = 0; 3095650a0e1Sdjm BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 3105650a0e1Sdjm BIO_printf(bio, "Content-Type: multipart/signed;"); 3115650a0e1Sdjm BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix); 3125650a0e1Sdjm BIO_puts(bio, " micalg=\""); 3135650a0e1Sdjm asn1_write_micalg(bio, mdalgs); 3145650a0e1Sdjm BIO_printf(bio, "\"; boundary=\"----%s\"%s%s", 3155650a0e1Sdjm bound, mime_eol, mime_eol); 3165650a0e1Sdjm BIO_printf(bio, "This is an S/MIME signed message%s%s", 3175650a0e1Sdjm mime_eol, mime_eol); 3185650a0e1Sdjm /* Now write out the first part */ 3195650a0e1Sdjm BIO_printf(bio, "------%s%s", bound, mime_eol); 3200a5d6edeSdjm if (!asn1_output_data(bio, data, val, flags, it)) 3215650a0e1Sdjm return 0; 3225650a0e1Sdjm BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol); 3235650a0e1Sdjm 3245650a0e1Sdjm /* Headers for signature */ 3255650a0e1Sdjm 3265650a0e1Sdjm BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 3275650a0e1Sdjm BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol); 3285650a0e1Sdjm BIO_printf(bio, "Content-Transfer-Encoding: base64%s", 3295650a0e1Sdjm mime_eol); 3305650a0e1Sdjm BIO_printf(bio, "Content-Disposition: attachment;"); 3315650a0e1Sdjm BIO_printf(bio, " filename=\"smime.p7s\"%s%s", 3325650a0e1Sdjm mime_eol, mime_eol); 3335650a0e1Sdjm B64_write_ASN1(bio, val, NULL, 0, it); 3345650a0e1Sdjm BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound, 3355650a0e1Sdjm mime_eol, mime_eol); 3365650a0e1Sdjm return 1; 3375650a0e1Sdjm } 3385650a0e1Sdjm 3395650a0e1Sdjm /* Determine smime-type header */ 3405650a0e1Sdjm 3415650a0e1Sdjm if (ctype_nid == NID_pkcs7_enveloped) 3425650a0e1Sdjm msg_type = "enveloped-data"; 343cc777fd4Stedu else if (ctype_nid == NID_pkcs7_signed) { 3445650a0e1Sdjm if (econt_nid == NID_id_smime_ct_receipt) 3455650a0e1Sdjm msg_type = "signed-receipt"; 3465650a0e1Sdjm else if (sk_X509_ALGOR_num(mdalgs) >= 0) 3475650a0e1Sdjm msg_type = "signed-data"; 3485650a0e1Sdjm else 3495650a0e1Sdjm msg_type = "certs-only"; 350cc777fd4Stedu } else if (ctype_nid == NID_id_smime_ct_compressedData) { 3515650a0e1Sdjm msg_type = "compressed-data"; 3525650a0e1Sdjm cname = "smime.p7z"; 3535650a0e1Sdjm } 3545650a0e1Sdjm /* MIME headers */ 3555650a0e1Sdjm BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 3565650a0e1Sdjm BIO_printf(bio, "Content-Disposition: attachment;"); 3575650a0e1Sdjm BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol); 3585650a0e1Sdjm BIO_printf(bio, "Content-Type: %smime;", mime_prefix); 3595650a0e1Sdjm if (msg_type) 3605650a0e1Sdjm BIO_printf(bio, " smime-type=%s;", msg_type); 3615650a0e1Sdjm BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol); 3625650a0e1Sdjm BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s", 3635650a0e1Sdjm mime_eol, mime_eol); 3645650a0e1Sdjm if (!B64_write_ASN1(bio, val, data, flags, it)) 3655650a0e1Sdjm return 0; 3665650a0e1Sdjm BIO_printf(bio, "%s", mime_eol); 3675650a0e1Sdjm return 1; 3685650a0e1Sdjm } 3695650a0e1Sdjm 3705650a0e1Sdjm /* Handle output of ASN1 data */ 3715650a0e1Sdjm 3725650a0e1Sdjm 373f309b230Sjsing static int 374f309b230Sjsing asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 3755650a0e1Sdjm const ASN1_ITEM *it) 3765650a0e1Sdjm { 3775650a0e1Sdjm BIO *tmpbio; 3785650a0e1Sdjm const ASN1_AUX *aux = it->funcs; 3795650a0e1Sdjm ASN1_STREAM_ARG sarg; 3805cdd308eSdjm int rv = 1; 3815650a0e1Sdjm 382*c5925a46Stb /* 383*c5925a46Stb * If data is not detached or resigning then the output BIO is 3845cdd308eSdjm * already set up to finalise when it is written through. 3855cdd308eSdjm */ 386cc777fd4Stedu if (!(flags & SMIME_DETACHED) || (flags & PKCS7_REUSE_DIGEST)) { 3875650a0e1Sdjm SMIME_crlf_copy(data, out, flags); 3885650a0e1Sdjm return 1; 3895650a0e1Sdjm } 3905650a0e1Sdjm 391cc777fd4Stedu if (!aux || !aux->asn1_cb) { 3925067ae9fSbeck ASN1error(ASN1_R_STREAMING_NOT_SUPPORTED); 3935650a0e1Sdjm return 0; 3945650a0e1Sdjm } 3955650a0e1Sdjm 3965650a0e1Sdjm sarg.out = out; 3975650a0e1Sdjm sarg.ndef_bio = NULL; 3985650a0e1Sdjm sarg.boundary = NULL; 3995650a0e1Sdjm 4005650a0e1Sdjm /* Let ASN1 code prepend any needed BIOs */ 4015650a0e1Sdjm 4025650a0e1Sdjm if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0) 4035650a0e1Sdjm return 0; 4045650a0e1Sdjm 4055650a0e1Sdjm /* Copy data across, passing through filter BIOs for processing */ 4065650a0e1Sdjm SMIME_crlf_copy(data, sarg.ndef_bio, flags); 4075650a0e1Sdjm 4085650a0e1Sdjm /* Finalize structure */ 4095650a0e1Sdjm if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0) 4105cdd308eSdjm rv = 0; 4115650a0e1Sdjm 4125650a0e1Sdjm /* Now remove any digests prepended to the BIO */ 4135650a0e1Sdjm 414cc777fd4Stedu while (sarg.ndef_bio != out) { 4155650a0e1Sdjm tmpbio = BIO_pop(sarg.ndef_bio); 4165650a0e1Sdjm BIO_free(sarg.ndef_bio); 4175650a0e1Sdjm sarg.ndef_bio = tmpbio; 4185650a0e1Sdjm } 4195650a0e1Sdjm 4205cdd308eSdjm return rv; 4215650a0e1Sdjm } 4225650a0e1Sdjm 4235650a0e1Sdjm /* SMIME reader: handle multipart/signed and opaque signing. 4245650a0e1Sdjm * in multipart case the content is placed in a memory BIO 4255650a0e1Sdjm * pointed to by "bcont". In opaque this is set to NULL 4265650a0e1Sdjm */ 4275650a0e1Sdjm 428f309b230Sjsing ASN1_VALUE * 429f309b230Sjsing SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it) 4305650a0e1Sdjm { 4315650a0e1Sdjm BIO *asnin; 4325650a0e1Sdjm STACK_OF(MIME_HEADER) *headers = NULL; 4335650a0e1Sdjm STACK_OF(BIO) *parts = NULL; 4345650a0e1Sdjm MIME_HEADER *hdr; 4355650a0e1Sdjm MIME_PARAM *prm; 4365650a0e1Sdjm ASN1_VALUE *val; 4375650a0e1Sdjm int ret; 4385650a0e1Sdjm 439f309b230Sjsing if (bcont) 440f309b230Sjsing *bcont = NULL; 4415650a0e1Sdjm 4425650a0e1Sdjm if (!(headers = mime_parse_hdr(bio))) { 4435067ae9fSbeck ASN1error(ASN1_R_MIME_PARSE_ERROR); 4445650a0e1Sdjm return NULL; 4455650a0e1Sdjm } 4465650a0e1Sdjm 4475650a0e1Sdjm if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 4485650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4495067ae9fSbeck ASN1error(ASN1_R_NO_CONTENT_TYPE); 4505650a0e1Sdjm return NULL; 4515650a0e1Sdjm } 4525650a0e1Sdjm 4535650a0e1Sdjm /* Handle multipart/signed */ 4545650a0e1Sdjm 4555650a0e1Sdjm if (!strcmp(hdr->value, "multipart/signed")) { 4565650a0e1Sdjm /* Split into two parts */ 4575650a0e1Sdjm prm = mime_param_find(hdr, "boundary"); 4585650a0e1Sdjm if (!prm || !prm->param_value) { 4595650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4605067ae9fSbeck ASN1error(ASN1_R_NO_MULTIPART_BOUNDARY); 4615650a0e1Sdjm return NULL; 4625650a0e1Sdjm } 4635650a0e1Sdjm ret = multi_split(bio, prm->param_value, &parts); 4645650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4655650a0e1Sdjm if (!ret || (sk_BIO_num(parts) != 2) ) { 4665067ae9fSbeck ASN1error(ASN1_R_NO_MULTIPART_BODY_FAILURE); 4675650a0e1Sdjm sk_BIO_pop_free(parts, BIO_vfree); 4685650a0e1Sdjm return NULL; 4695650a0e1Sdjm } 4705650a0e1Sdjm 4715650a0e1Sdjm /* Parse the signature piece */ 4725650a0e1Sdjm asnin = sk_BIO_value(parts, 1); 4735650a0e1Sdjm 4745650a0e1Sdjm if (!(headers = mime_parse_hdr(asnin))) { 4755067ae9fSbeck ASN1error(ASN1_R_MIME_SIG_PARSE_ERROR); 4765650a0e1Sdjm sk_BIO_pop_free(parts, BIO_vfree); 4775650a0e1Sdjm return NULL; 4785650a0e1Sdjm } 4795650a0e1Sdjm 4805650a0e1Sdjm /* Get content type */ 4815650a0e1Sdjm 4825650a0e1Sdjm if (!(hdr = mime_hdr_find(headers, "content-type")) || 4835650a0e1Sdjm !hdr->value) { 4845650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4850bcb7125Smiod sk_BIO_pop_free(parts, BIO_vfree); 4865067ae9fSbeck ASN1error(ASN1_R_NO_SIG_CONTENT_TYPE); 4875650a0e1Sdjm return NULL; 4885650a0e1Sdjm } 4895650a0e1Sdjm 4905650a0e1Sdjm if (strcmp(hdr->value, "application/x-pkcs7-signature") && 4915650a0e1Sdjm strcmp(hdr->value, "application/pkcs7-signature")) { 4925067ae9fSbeck ASN1error(ASN1_R_SIG_INVALID_MIME_TYPE); 4930f637b92Sbeck ERR_asprintf_error_data("type: %s", hdr->value); 4945cdd308eSdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4955650a0e1Sdjm sk_BIO_pop_free(parts, BIO_vfree); 4965650a0e1Sdjm return NULL; 4975650a0e1Sdjm } 4985650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 4995650a0e1Sdjm /* Read in ASN1 */ 5005650a0e1Sdjm if (!(val = b64_read_asn1(asnin, it))) { 5015067ae9fSbeck ASN1error(ASN1_R_ASN1_SIG_PARSE_ERROR); 5025650a0e1Sdjm sk_BIO_pop_free(parts, BIO_vfree); 5035650a0e1Sdjm return NULL; 5045650a0e1Sdjm } 5055650a0e1Sdjm 5065650a0e1Sdjm if (bcont) { 5075650a0e1Sdjm *bcont = sk_BIO_value(parts, 0); 5085650a0e1Sdjm BIO_free(asnin); 5095650a0e1Sdjm sk_BIO_free(parts); 5105650a0e1Sdjm } else sk_BIO_pop_free(parts, BIO_vfree); 5115650a0e1Sdjm return val; 5125650a0e1Sdjm } 5135650a0e1Sdjm 5145650a0e1Sdjm /* OK, if not multipart/signed try opaque signature */ 5155650a0e1Sdjm 5165650a0e1Sdjm if (strcmp (hdr->value, "application/x-pkcs7-mime") && 5175650a0e1Sdjm strcmp (hdr->value, "application/pkcs7-mime")) { 5185067ae9fSbeck ASN1error(ASN1_R_INVALID_MIME_TYPE); 5190f637b92Sbeck ERR_asprintf_error_data("type: %s", hdr->value); 5205650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 5215650a0e1Sdjm return NULL; 5225650a0e1Sdjm } 5235650a0e1Sdjm 5245650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 5255650a0e1Sdjm 5265650a0e1Sdjm if (!(val = b64_read_asn1(bio, it))) { 5275067ae9fSbeck ASN1error(ASN1_R_ASN1_PARSE_ERROR); 5285650a0e1Sdjm return NULL; 5295650a0e1Sdjm } 5305650a0e1Sdjm return val; 5315650a0e1Sdjm } 5325650a0e1Sdjm 5335650a0e1Sdjm /* Copy text from one BIO to another making the output CRLF at EOL */ 534f309b230Sjsing int 535f309b230Sjsing SMIME_crlf_copy(BIO *in, BIO *out, int flags) 5365650a0e1Sdjm { 5375650a0e1Sdjm BIO *bf; 5385650a0e1Sdjm char eol; 5395650a0e1Sdjm int len; 5405650a0e1Sdjm char linebuf[MAX_SMLEN]; 541f309b230Sjsing 5425650a0e1Sdjm /* Buffer output so we don't write one line at a time. This is 5435650a0e1Sdjm * useful when streaming as we don't end up with one OCTET STRING 5445650a0e1Sdjm * per line. 5455650a0e1Sdjm */ 5465650a0e1Sdjm bf = BIO_new(BIO_f_buffer()); 5475650a0e1Sdjm if (!bf) 5485650a0e1Sdjm return 0; 5495650a0e1Sdjm out = BIO_push(bf, out); 550cc777fd4Stedu if (flags & SMIME_BINARY) { 5515650a0e1Sdjm while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 5525650a0e1Sdjm BIO_write(out, linebuf, len); 553cc777fd4Stedu } else { 5545650a0e1Sdjm if (flags & SMIME_TEXT) 5555650a0e1Sdjm BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 556cc777fd4Stedu while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 5575650a0e1Sdjm eol = strip_eol(linebuf, &len); 5585650a0e1Sdjm if (len) 5595650a0e1Sdjm BIO_write(out, linebuf, len); 560f309b230Sjsing if (eol) 561f309b230Sjsing BIO_write(out, "\r\n", 2); 5625650a0e1Sdjm } 5635650a0e1Sdjm } 5645650a0e1Sdjm (void)BIO_flush(out); 5655650a0e1Sdjm BIO_pop(out); 5665650a0e1Sdjm BIO_free(bf); 5675650a0e1Sdjm return 1; 5685650a0e1Sdjm } 569acf64401Sbeck LCRYPTO_ALIAS(SMIME_crlf_copy); 5705650a0e1Sdjm 5715650a0e1Sdjm /* Strip off headers if they are text/plain */ 572f309b230Sjsing int 573f309b230Sjsing SMIME_text(BIO *in, BIO *out) 5745650a0e1Sdjm { 5755650a0e1Sdjm char iobuf[4096]; 5765650a0e1Sdjm int len; 5775650a0e1Sdjm STACK_OF(MIME_HEADER) *headers; 5785650a0e1Sdjm MIME_HEADER *hdr; 5795650a0e1Sdjm 5805650a0e1Sdjm if (!(headers = mime_parse_hdr(in))) { 5815067ae9fSbeck ASN1error(ASN1_R_MIME_PARSE_ERROR); 5825650a0e1Sdjm return 0; 5835650a0e1Sdjm } 5845650a0e1Sdjm if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 5855067ae9fSbeck ASN1error(ASN1_R_MIME_NO_CONTENT_TYPE); 5865650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 5875650a0e1Sdjm return 0; 5885650a0e1Sdjm } 5895650a0e1Sdjm if (strcmp (hdr->value, "text/plain")) { 5905067ae9fSbeck ASN1error(ASN1_R_INVALID_MIME_TYPE); 5910f637b92Sbeck ERR_asprintf_error_data("type: %s", hdr->value); 5925650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 5935650a0e1Sdjm return 0; 5945650a0e1Sdjm } 5955650a0e1Sdjm sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 5965650a0e1Sdjm while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 5975650a0e1Sdjm BIO_write(out, iobuf, len); 5988214bb00Sdjm if (len < 0) 5998214bb00Sdjm return 0; 6005650a0e1Sdjm return 1; 6015650a0e1Sdjm } 602acf64401Sbeck LCRYPTO_ALIAS(SMIME_text); 6035650a0e1Sdjm 6040bcb7125Smiod /* 6050bcb7125Smiod * Split a multipart/XXX message body into component parts: result is 6065650a0e1Sdjm * canonical parts in a STACK of bios 6075650a0e1Sdjm */ 608f309b230Sjsing static int 609f309b230Sjsing multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) 6105650a0e1Sdjm { 6115650a0e1Sdjm char linebuf[MAX_SMLEN]; 6125650a0e1Sdjm int len, blen; 6135650a0e1Sdjm int eol = 0, next_eol = 0; 6145650a0e1Sdjm BIO *bpart = NULL; 6155650a0e1Sdjm STACK_OF(BIO) *parts; 6165650a0e1Sdjm char state, part, first; 6175650a0e1Sdjm 6185650a0e1Sdjm blen = strlen(bound); 6195650a0e1Sdjm part = 0; 6205650a0e1Sdjm state = 0; 6215650a0e1Sdjm first = 1; 6225650a0e1Sdjm parts = sk_BIO_new_null(); 6235650a0e1Sdjm *ret = parts; 6240bcb7125Smiod if (parts == NULL) 6250bcb7125Smiod return 0; 6265650a0e1Sdjm while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 6275650a0e1Sdjm state = mime_bound_check(linebuf, len, bound, blen); 6285650a0e1Sdjm if (state == 1) { 6295650a0e1Sdjm first = 1; 6305650a0e1Sdjm part++; 6315650a0e1Sdjm } else if (state == 2) { 6320bcb7125Smiod if (sk_BIO_push(parts, bpart) == 0) 6330bcb7125Smiod return 0; 6345650a0e1Sdjm return 1; 6355650a0e1Sdjm } else if (part) { 6365650a0e1Sdjm /* Strip CR+LF from linebuf */ 6375650a0e1Sdjm next_eol = strip_eol(linebuf, &len); 6385650a0e1Sdjm if (first) { 6395650a0e1Sdjm first = 0; 6400bcb7125Smiod if (bpart != NULL) { 6410bcb7125Smiod if (sk_BIO_push(parts, bpart) == 0) 6420bcb7125Smiod return 0; 6430bcb7125Smiod } 6445650a0e1Sdjm bpart = BIO_new(BIO_s_mem()); 6450bcb7125Smiod if (bpart == NULL) 6460bcb7125Smiod return 0; 6475650a0e1Sdjm BIO_set_mem_eof_return(bpart, 0); 6485650a0e1Sdjm } else if (eol) 6495650a0e1Sdjm BIO_write(bpart, "\r\n", 2); 6505650a0e1Sdjm eol = next_eol; 6515650a0e1Sdjm if (len) 6525650a0e1Sdjm BIO_write(bpart, linebuf, len); 6535650a0e1Sdjm } 6545650a0e1Sdjm } 6550bcb7125Smiod BIO_free(bpart); 6565650a0e1Sdjm return 0; 6575650a0e1Sdjm } 6585650a0e1Sdjm 6595650a0e1Sdjm /* This is the big one: parse MIME header lines up to message body */ 6605650a0e1Sdjm 6615650a0e1Sdjm #define MIME_INVALID 0 6625650a0e1Sdjm #define MIME_START 1 6635650a0e1Sdjm #define MIME_TYPE 2 6645650a0e1Sdjm #define MIME_NAME 3 6655650a0e1Sdjm #define MIME_VALUE 4 6665650a0e1Sdjm #define MIME_QUOTE 5 6675650a0e1Sdjm #define MIME_COMMENT 6 6685650a0e1Sdjm 6694c56962aStb static STACK_OF(MIME_HEADER) * 6704c56962aStb mime_parse_hdr(BIO *bio) 6715650a0e1Sdjm { 6725650a0e1Sdjm char *p, *q, c; 6735650a0e1Sdjm char *ntmp; 6745650a0e1Sdjm char linebuf[MAX_SMLEN]; 6755650a0e1Sdjm MIME_HEADER *mhdr = NULL; 6765650a0e1Sdjm STACK_OF(MIME_HEADER) *headers; 6775650a0e1Sdjm int len, state, save_state = 0; 6785650a0e1Sdjm 6795650a0e1Sdjm headers = sk_MIME_HEADER_new(mime_hdr_cmp); 680fe7219aeSmiod if (!headers) 681fe7219aeSmiod return NULL; 6825650a0e1Sdjm while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 6835650a0e1Sdjm /* If whitespace at line start then continuation line */ 684f309b230Sjsing if (mhdr && isspace((unsigned char)linebuf[0])) 685f309b230Sjsing state = MIME_NAME; 686f309b230Sjsing else 687f309b230Sjsing state = MIME_START; 6885650a0e1Sdjm ntmp = NULL; 689f309b230Sjsing 6905650a0e1Sdjm /* Go through all characters */ 691f309b230Sjsing for (p = linebuf, q = linebuf; 692f309b230Sjsing (c = *p) && (c != '\r') && (c != '\n'); p++) { 6935650a0e1Sdjm 6945650a0e1Sdjm /* State machine to handle MIME headers 6955650a0e1Sdjm * if this looks horrible that's because it *is* 6965650a0e1Sdjm */ 6975650a0e1Sdjm 6985650a0e1Sdjm switch (state) { 6995650a0e1Sdjm case MIME_START: 7005650a0e1Sdjm if (c == ':') { 7015650a0e1Sdjm state = MIME_TYPE; 7025650a0e1Sdjm *p = 0; 7035650a0e1Sdjm ntmp = strip_ends(q); 7045650a0e1Sdjm q = p + 1; 7055650a0e1Sdjm } 7065650a0e1Sdjm break; 7075650a0e1Sdjm 7085650a0e1Sdjm case MIME_TYPE: 7095650a0e1Sdjm if (c == ';') { 7105650a0e1Sdjm *p = 0; 711f309b230Sjsing mhdr = mime_hdr_new(ntmp, 712f309b230Sjsing strip_ends(q)); 7130bcb7125Smiod if (mhdr == NULL) 7140bcb7125Smiod goto merr; 7150bcb7125Smiod if (sk_MIME_HEADER_push(headers, 7160bcb7125Smiod mhdr) == 0) 7170bcb7125Smiod goto merr; 7185650a0e1Sdjm ntmp = NULL; 7195650a0e1Sdjm q = p + 1; 7205650a0e1Sdjm state = MIME_NAME; 7215650a0e1Sdjm } else if (c == '(') { 7225650a0e1Sdjm save_state = state; 7235650a0e1Sdjm state = MIME_COMMENT; 7245650a0e1Sdjm } 7255650a0e1Sdjm break; 7265650a0e1Sdjm 7275650a0e1Sdjm case MIME_COMMENT: 7285650a0e1Sdjm if (c == ')') { 7295650a0e1Sdjm state = save_state; 7305650a0e1Sdjm } 7315650a0e1Sdjm break; 7325650a0e1Sdjm 7335650a0e1Sdjm case MIME_NAME: 7345650a0e1Sdjm if (c == '=') { 7355650a0e1Sdjm state = MIME_VALUE; 7365650a0e1Sdjm *p = 0; 7375650a0e1Sdjm ntmp = strip_ends(q); 7385650a0e1Sdjm q = p + 1; 7395650a0e1Sdjm } 7405650a0e1Sdjm break; 7415650a0e1Sdjm 7425650a0e1Sdjm case MIME_VALUE: 7435650a0e1Sdjm if (c == ';') { 7445650a0e1Sdjm state = MIME_NAME; 7455650a0e1Sdjm *p = 0; 746f309b230Sjsing mime_hdr_addparam(mhdr, ntmp, 747f309b230Sjsing strip_ends(q)); 7485650a0e1Sdjm ntmp = NULL; 7495650a0e1Sdjm q = p + 1; 7505650a0e1Sdjm } else if (c == '"') { 7515650a0e1Sdjm state = MIME_QUOTE; 7525650a0e1Sdjm } else if (c == '(') { 7535650a0e1Sdjm save_state = state; 7545650a0e1Sdjm state = MIME_COMMENT; 7555650a0e1Sdjm } 7565650a0e1Sdjm break; 7575650a0e1Sdjm 7585650a0e1Sdjm case MIME_QUOTE: 7595650a0e1Sdjm if (c == '"') { 7605650a0e1Sdjm state = MIME_VALUE; 7615650a0e1Sdjm } 7625650a0e1Sdjm break; 7635650a0e1Sdjm } 7645650a0e1Sdjm } 7655650a0e1Sdjm 7665650a0e1Sdjm if (state == MIME_TYPE) { 7675650a0e1Sdjm mhdr = mime_hdr_new(ntmp, strip_ends(q)); 7680bcb7125Smiod if (mhdr == NULL) 7690bcb7125Smiod goto merr; 7700bcb7125Smiod if (sk_MIME_HEADER_push(headers, mhdr) == 0) 7710bcb7125Smiod goto merr; 7725650a0e1Sdjm } else if (state == MIME_VALUE) 7735650a0e1Sdjm mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 774f309b230Sjsing 775f309b230Sjsing if (p == linebuf) 776f309b230Sjsing break; /* Blank line means end of headers */ 7775650a0e1Sdjm } 7785650a0e1Sdjm 7795650a0e1Sdjm return headers; 7800bcb7125Smiod 7810bcb7125Smiod merr: 7820bcb7125Smiod if (mhdr != NULL) 7830bcb7125Smiod mime_hdr_free(mhdr); 7840bcb7125Smiod sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 7850bcb7125Smiod return NULL; 7865650a0e1Sdjm } 7875650a0e1Sdjm 788f309b230Sjsing static char * 789f309b230Sjsing strip_ends(char *name) 7905650a0e1Sdjm { 7915650a0e1Sdjm return strip_end(strip_start(name)); 7925650a0e1Sdjm } 7935650a0e1Sdjm 7945650a0e1Sdjm /* Strip a parameter of whitespace from start of param */ 795f309b230Sjsing static char * 796f309b230Sjsing strip_start(char *name) 7975650a0e1Sdjm { 7985650a0e1Sdjm char *p, c; 799f309b230Sjsing 8005650a0e1Sdjm /* Look for first non white space or quote */ 8015650a0e1Sdjm for (p = name; (c = *p); p++) { 8025650a0e1Sdjm if (c == '"') { 8035650a0e1Sdjm /* Next char is start of string if non null */ 804f309b230Sjsing if (p[1]) 805f309b230Sjsing return p + 1; 8065650a0e1Sdjm /* Else null string */ 8075650a0e1Sdjm return NULL; 8085650a0e1Sdjm } 809f309b230Sjsing if (!isspace((unsigned char)c)) 810f309b230Sjsing return p; 8115650a0e1Sdjm } 8125650a0e1Sdjm return NULL; 8135650a0e1Sdjm } 8145650a0e1Sdjm 8155650a0e1Sdjm /* As above but strip from end of string : maybe should handle brackets? */ 816f309b230Sjsing static char * 817f309b230Sjsing strip_end(char *name) 8185650a0e1Sdjm { 8195650a0e1Sdjm char *p, c; 820f309b230Sjsing 821f309b230Sjsing if (!name) 822f309b230Sjsing return NULL; 823f309b230Sjsing 8245650a0e1Sdjm /* Look for first non white space or quote */ 8255650a0e1Sdjm for (p = name + strlen(name) - 1; p >= name; p--) { 8265650a0e1Sdjm c = *p; 8275650a0e1Sdjm if (c == '"') { 828f309b230Sjsing if (p - 1 == name) 829f309b230Sjsing return NULL; 8305650a0e1Sdjm *p = 0; 8315650a0e1Sdjm return name; 8325650a0e1Sdjm } 833f309b230Sjsing if (isspace((unsigned char)c)) 834f309b230Sjsing *p = 0; 835f309b230Sjsing else 836f309b230Sjsing return name; 8375650a0e1Sdjm } 8385650a0e1Sdjm return NULL; 8395650a0e1Sdjm } 8405650a0e1Sdjm 841f309b230Sjsing static MIME_HEADER * 842f309b230Sjsing mime_hdr_new(char *name, char *value) 8435650a0e1Sdjm { 8445650a0e1Sdjm MIME_HEADER *mhdr; 8454f8af24fSmiod char *tmpname = NULL, *tmpval = NULL, *p; 8464f8af24fSmiod 8475650a0e1Sdjm if (name) { 84874a2cbdcSbeck if (!(tmpname = strdup(name))) 8494f8af24fSmiod goto err; 850a11e03a0Sderaadt for (p = tmpname; *p; p++) 851a11e03a0Sderaadt *p = tolower((unsigned char)*p); 8524f8af24fSmiod } 8535650a0e1Sdjm if (value) { 85474a2cbdcSbeck if (!(tmpval = strdup(value))) 8554f8af24fSmiod goto err; 856a11e03a0Sderaadt for (p = tmpval; *p; p++) 857a11e03a0Sderaadt *p = tolower((unsigned char)*p); 858603b910fSjsg } 8594f8af24fSmiod mhdr = malloc(sizeof(MIME_HEADER)); 8604f8af24fSmiod if (!mhdr) 8614f8af24fSmiod goto err; 8625650a0e1Sdjm mhdr->name = tmpname; 8635650a0e1Sdjm mhdr->value = tmpval; 86464b5e5f5Sjsg if (!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) { 86564b5e5f5Sjsg free(mhdr); 8664f8af24fSmiod goto err; 86764b5e5f5Sjsg } 8685650a0e1Sdjm return mhdr; 8694f8af24fSmiod err: 8704f8af24fSmiod free(tmpname); 8714f8af24fSmiod free(tmpval); 8724f8af24fSmiod return NULL; 8735650a0e1Sdjm } 8745650a0e1Sdjm 875f309b230Sjsing static int 876f309b230Sjsing mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value) 8775650a0e1Sdjm { 8784f8af24fSmiod char *tmpname = NULL, *tmpval = NULL, *p; 8795650a0e1Sdjm MIME_PARAM *mparam; 880f309b230Sjsing 8815650a0e1Sdjm if (name) { 88274a2cbdcSbeck tmpname = strdup(name); 883f309b230Sjsing if (!tmpname) 8844f8af24fSmiod goto err; 885a11e03a0Sderaadt for (p = tmpname; *p; p++) 886a11e03a0Sderaadt *p = tolower((unsigned char)*p); 8874f8af24fSmiod } 8885650a0e1Sdjm if (value) { 88974a2cbdcSbeck tmpval = strdup(value); 890f309b230Sjsing if (!tmpval) 8914f8af24fSmiod goto err; 8924f8af24fSmiod } 8935650a0e1Sdjm /* Parameter values are case sensitive so leave as is */ 8946dae11f4Sderaadt mparam = malloc(sizeof(MIME_PARAM)); 895f309b230Sjsing if (!mparam) 8964f8af24fSmiod goto err; 8975650a0e1Sdjm mparam->param_name = tmpname; 8985650a0e1Sdjm mparam->param_value = tmpval; 8990bcb7125Smiod if (sk_MIME_PARAM_push(mhdr->params, mparam) == 0) { 9000bcb7125Smiod free(mparam); 9010bcb7125Smiod goto err; 9020bcb7125Smiod } 9035650a0e1Sdjm return 1; 9044f8af24fSmiod err: 9054f8af24fSmiod free(tmpname); 9064f8af24fSmiod free(tmpval); 9074f8af24fSmiod return 0; 9085650a0e1Sdjm } 9095650a0e1Sdjm 910f309b230Sjsing static int 911f309b230Sjsing mime_hdr_cmp(const MIME_HEADER * const *a, const MIME_HEADER * const *b) 9125650a0e1Sdjm { 9135cdd308eSdjm if (!(*a)->name || !(*b)->name) 9145cdd308eSdjm return !!(*a)->name - !!(*b)->name; 9155650a0e1Sdjm return (strcmp((*a)->name, (*b)->name)); 9165650a0e1Sdjm } 9175650a0e1Sdjm 918f309b230Sjsing static int 919f309b230Sjsing mime_param_cmp(const MIME_PARAM * const *a, const MIME_PARAM * const *b) 9205650a0e1Sdjm { 9215cdd308eSdjm if (!(*a)->param_name || !(*b)->param_name) 9225cdd308eSdjm return !!(*a)->param_name - !!(*b)->param_name; 9235650a0e1Sdjm return (strcmp((*a)->param_name, (*b)->param_name)); 9245650a0e1Sdjm } 9255650a0e1Sdjm 9265650a0e1Sdjm /* Find a header with a given name (if possible) */ 9275650a0e1Sdjm 928f309b230Sjsing static MIME_HEADER * 929f309b230Sjsing mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name) 9305650a0e1Sdjm { 9315650a0e1Sdjm MIME_HEADER htmp; 9325650a0e1Sdjm int idx; 9335650a0e1Sdjm htmp.name = name; 9345650a0e1Sdjm idx = sk_MIME_HEADER_find(hdrs, &htmp); 935f309b230Sjsing if (idx < 0) 936f309b230Sjsing return NULL; 9375650a0e1Sdjm return sk_MIME_HEADER_value(hdrs, idx); 9385650a0e1Sdjm } 9395650a0e1Sdjm 940f309b230Sjsing static MIME_PARAM * 941f309b230Sjsing mime_param_find(MIME_HEADER *hdr, char *name) 9425650a0e1Sdjm { 9435650a0e1Sdjm MIME_PARAM param; 9445650a0e1Sdjm int idx; 9455650a0e1Sdjm param.param_name = name; 9465650a0e1Sdjm idx = sk_MIME_PARAM_find(hdr->params, ¶m); 947f309b230Sjsing if (idx < 0) 948f309b230Sjsing return NULL; 9495650a0e1Sdjm return sk_MIME_PARAM_value(hdr->params, idx); 9505650a0e1Sdjm } 9515650a0e1Sdjm 952f309b230Sjsing static void 953f309b230Sjsing mime_hdr_free(MIME_HEADER *hdr) 9545650a0e1Sdjm { 955f309b230Sjsing free(hdr->name); 956f309b230Sjsing free(hdr->value); 957f309b230Sjsing if (hdr->params) 958f309b230Sjsing sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 9596f3a6cb1Sbeck free(hdr); 9605650a0e1Sdjm } 9615650a0e1Sdjm 962f309b230Sjsing static void 963f309b230Sjsing mime_param_free(MIME_PARAM *param) 9645650a0e1Sdjm { 965f309b230Sjsing free(param->param_name); 966f309b230Sjsing free(param->param_value); 9676f3a6cb1Sbeck free(param); 9685650a0e1Sdjm } 9695650a0e1Sdjm 9705650a0e1Sdjm /* Check for a multipart boundary. Returns: 9715650a0e1Sdjm * 0 : no boundary 9725650a0e1Sdjm * 1 : part boundary 9735650a0e1Sdjm * 2 : final boundary 9745650a0e1Sdjm */ 975f309b230Sjsing static int 976f309b230Sjsing mime_bound_check(char *line, int linelen, char *bound, int blen) 9775650a0e1Sdjm { 978f309b230Sjsing if (linelen == -1) 979f309b230Sjsing linelen = strlen(line); 980f309b230Sjsing if (blen == -1) 981f309b230Sjsing blen = strlen(bound); 9825650a0e1Sdjm /* Quickly eliminate if line length too short */ 983f309b230Sjsing if (blen + 2 > linelen) 984f309b230Sjsing return 0; 9855650a0e1Sdjm /* Check for part boundary */ 9865650a0e1Sdjm if (!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) { 987f309b230Sjsing if (!strncmp(line + blen + 2, "--", 2)) 988f309b230Sjsing return 2; 989f309b230Sjsing else 990f309b230Sjsing return 1; 9915650a0e1Sdjm } 9925650a0e1Sdjm return 0; 9935650a0e1Sdjm } 9945650a0e1Sdjm 995f309b230Sjsing static int 996f309b230Sjsing strip_eol(char *linebuf, int *plen) 9975650a0e1Sdjm { 9985650a0e1Sdjm int len = *plen; 9995650a0e1Sdjm char *p, c; 10005650a0e1Sdjm int is_eol = 0; 10010fe6bc7fStedu 1002cc777fd4Stedu for (p = linebuf + len - 1; len > 0; len--, p--) { 10035650a0e1Sdjm c = *p; 10045650a0e1Sdjm if (c == '\n') 10055650a0e1Sdjm is_eol = 1; 10065650a0e1Sdjm else if (c != '\r') 10075650a0e1Sdjm break; 10085650a0e1Sdjm } 10095650a0e1Sdjm *plen = len; 10105650a0e1Sdjm return is_eol; 10115650a0e1Sdjm } 1012