LCOV - code coverage report
Current view: top level - third_party/openssl/crypto/asn1 - asn_mime.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 0 388 0.0 %
Date: 2015-10-10 Functions: 0 23 0.0 %

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

Generated by: LCOV version 1.10