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