~ubuntu-branches/ubuntu/lucid/openssl/lucid-proposed

« back to all changes in this revision

Viewing changes to crypto/asn1/asn_mime.c

  • Committer: Bazaar Package Importer
  • Author(s): Kurt Roeckx
  • Date: 2009-06-13 18:15:46 UTC
  • mto: (11.1.5 squeeze)
  • mto: This revision was merged to the branch mainline in revision 34.
  • Revision ID: james.westby@ubuntu.com-20090613181546-vbfntai3b009dl1u
Tags: upstream-0.9.8k
ImportĀ upstreamĀ versionĀ 0.9.8k

Show diffs side-by-side

added added

removed removed

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