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