1 /* $OpenBSD: cms.c,v 1.7 2020/04/02 09:16:43 claudio Exp $ */ 2 /* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <assert.h> 19 #include <err.h> 20 #include <stdarg.h> 21 #include <stdint.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include <openssl/cms.h> 27 28 #include "extern.h" 29 30 /* 31 * Parse and validate a self-signed CMS message, where the signing X509 32 * certificate has been hashed to dgst (optional). 33 * Conforms to RFC 6488. 34 * The eContentType of the message must be an oid object. 35 * Return the eContent as a string and set "rsz" to be its length. 36 */ 37 unsigned char * 38 cms_parse_validate(X509 **xp, const char *fn, 39 const char *oid, const unsigned char *dgst, size_t *rsz) 40 { 41 const ASN1_OBJECT *obj; 42 ASN1_OCTET_STRING **os = NULL; 43 BIO *bio = NULL, *shamd; 44 CMS_ContentInfo *cms; 45 FILE *f; 46 char buf[128], mdbuf[EVP_MAX_MD_SIZE]; 47 int rc = 0, sz; 48 STACK_OF(X509) *certs = NULL; 49 EVP_MD *md; 50 unsigned char *res = NULL; 51 52 *rsz = 0; 53 *xp = NULL; 54 55 /* 56 * This is usually fopen() failure, so let it pass through to 57 * the handler, which will in turn ignore the entity. 58 */ 59 if ((f = fopen(fn, "rb")) == NULL) { 60 warn("%s", fn); 61 return NULL; 62 } 63 64 if ((bio = BIO_new_fp(f, BIO_CLOSE)) == NULL) { 65 cryptowarnx("%s: BIO_new_fp", fn); 66 return NULL; 67 } 68 69 /* 70 * If we have a digest specified, create an MD chain that will 71 * automatically compute a digest during the CMS creation. 72 */ 73 74 if (dgst != NULL) { 75 if ((shamd = BIO_new(BIO_f_md())) == NULL) 76 cryptoerrx("BIO_new"); 77 if (!BIO_set_md(shamd, EVP_sha256())) 78 cryptoerrx("BIO_set_md"); 79 if ((bio = BIO_push(shamd, bio)) == NULL) 80 cryptoerrx("BIO_push"); 81 } 82 83 if ((cms = d2i_CMS_bio(bio, NULL)) == NULL) { 84 cryptowarnx("%s: RFC 6488: failed CMS parse", fn); 85 goto out; 86 } 87 88 /* 89 * If we have a digest, find it in the chain (we'll already have 90 * made it, so assert otherwise) and verify it. 91 */ 92 93 if (dgst != NULL) { 94 shamd = BIO_find_type(bio, BIO_TYPE_MD); 95 assert(shamd != NULL); 96 97 if (!BIO_get_md(shamd, &md)) 98 cryptoerrx("BIO_get_md"); 99 assert(EVP_MD_type(md) == NID_sha256); 100 101 if ((sz = BIO_gets(shamd, mdbuf, EVP_MAX_MD_SIZE)) < 0) 102 cryptoerrx("BIO_gets"); 103 assert(sz == SHA256_DIGEST_LENGTH); 104 105 if (memcmp(mdbuf, dgst, SHA256_DIGEST_LENGTH)) { 106 warnx("%s: RFC 6488: bad message digest", fn); 107 goto out; 108 } 109 } 110 111 /* 112 * The CMS is self-signed with a signing certifiate. 113 * Verify that the self-signage is correct. 114 */ 115 116 if (!CMS_verify(cms, NULL, NULL, 117 NULL, NULL, CMS_NO_SIGNER_CERT_VERIFY)) { 118 cryptowarnx("%s: RFC 6488: CMS not self-signed", fn); 119 goto out; 120 } 121 122 /* RFC 6488 section 2.1.3.1: check the object's eContentType. */ 123 124 obj = CMS_get0_eContentType(cms); 125 if ((sz = OBJ_obj2txt(buf, sizeof(buf), obj, 1)) < 0) 126 cryptoerrx("OBJ_obj2txt"); 127 128 if ((size_t)sz >= sizeof(buf)) { 129 warnx("%s: RFC 6488 section 2.1.3.1: " 130 "eContentType: OID too long", fn); 131 goto out; 132 } else if (strcmp(buf, oid)) { 133 warnx("%s: RFC 6488 section 2.1.3.1: eContentType: " 134 "unknown OID: %s, want %s", fn, buf, oid); 135 goto out; 136 } 137 138 /* 139 * The self-signing certificate is further signed by the input 140 * signing authority according to RFC 6488, 2.1.4. 141 * We extract that certificate now for later verification. 142 */ 143 144 certs = CMS_get0_signers(cms); 145 if (certs == NULL || sk_X509_num(certs) != 1) { 146 warnx("%s: RFC 6488 section 2.1.4: eContent: " 147 "want 1 signer, have %d", fn, sk_X509_num(certs)); 148 goto out; 149 } 150 *xp = X509_dup(sk_X509_value(certs, 0)); 151 152 /* Verify that we have eContent to disseminate. */ 153 154 if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) { 155 warnx("%s: RFC 6488 section 2.1.4: " 156 "eContent: zero-length content", fn); 157 goto out; 158 } 159 160 /* 161 * Extract and duplicate the eContent. 162 * The CMS framework offers us no other way of easily managing 163 * this information; and since we're going to d2i it anyway, 164 * simply pass it as the desired underlying types. 165 */ 166 167 if ((res = malloc((*os)->length)) == NULL) 168 err(1, NULL); 169 memcpy(res, (*os)->data, (*os)->length); 170 *rsz = (*os)->length; 171 172 rc = 1; 173 out: 174 BIO_free_all(bio); 175 sk_X509_free(certs); 176 CMS_ContentInfo_free(cms); 177 178 if (rc == 0) { 179 X509_free(*xp); 180 *xp = NULL; 181 } 182 183 return res; 184 } 185