asn_mime.c 30 KB

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