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