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