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