xref: /openbsd/usr.sbin/rpki-client/cms.c (revision 097a140d)
1 /*	$OpenBSD: cms.c,v 1.8 2021/01/29 10:13:16 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, size_t *rsz)
40 {
41 	const ASN1_OBJECT	*obj;
42 	ASN1_OCTET_STRING	**os = NULL;
43 	BIO			*bio = NULL;
44 	CMS_ContentInfo		*cms;
45 	FILE			*f;
46 	char			 buf[128];
47 	int			 rc = 0, sz;
48 	STACK_OF(X509)		*certs = NULL;
49 	unsigned char		*res = NULL;
50 
51 	*rsz = 0;
52 	*xp = NULL;
53 
54 	/*
55 	 * This is usually fopen() failure, so let it pass through to
56 	 * the handler, which will in turn ignore the entity.
57 	 */
58 	if ((f = fopen(fn, "rb")) == NULL) {
59 		warn("%s", fn);
60 		return NULL;
61 	}
62 
63 	if ((bio = BIO_new_fp(f, BIO_CLOSE)) == NULL) {
64 		cryptowarnx("%s: BIO_new_fp", fn);
65 		return NULL;
66 	}
67 
68 	if ((cms = d2i_CMS_bio(bio, NULL)) == NULL) {
69 		cryptowarnx("%s: RFC 6488: failed CMS parse", fn);
70 		goto out;
71 	}
72 
73 	/*
74 	 * The CMS is self-signed with a signing certifiate.
75 	 * Verify that the self-signage is correct.
76 	 */
77 
78 	if (!CMS_verify(cms, NULL, NULL,
79 	    NULL, NULL, CMS_NO_SIGNER_CERT_VERIFY)) {
80 		cryptowarnx("%s: RFC 6488: CMS not self-signed", fn);
81 		goto out;
82 	}
83 
84 	/* RFC 6488 section 2.1.3.1: check the object's eContentType. */
85 
86 	obj = CMS_get0_eContentType(cms);
87 	if ((sz = OBJ_obj2txt(buf, sizeof(buf), obj, 1)) < 0)
88 		cryptoerrx("OBJ_obj2txt");
89 
90 	if ((size_t)sz >= sizeof(buf)) {
91 		warnx("%s: RFC 6488 section 2.1.3.1: "
92 		    "eContentType: OID too long", fn);
93 		goto out;
94 	} else if (strcmp(buf, oid)) {
95 		warnx("%s: RFC 6488 section 2.1.3.1: eContentType: "
96 		    "unknown OID: %s, want %s", fn, buf, oid);
97 		goto out;
98 	}
99 
100 	/*
101 	 * The self-signing certificate is further signed by the input
102 	 * signing authority according to RFC 6488, 2.1.4.
103 	 * We extract that certificate now for later verification.
104 	 */
105 
106 	certs = CMS_get0_signers(cms);
107 	if (certs == NULL || sk_X509_num(certs) != 1) {
108 		warnx("%s: RFC 6488 section 2.1.4: eContent: "
109 		    "want 1 signer, have %d", fn, sk_X509_num(certs));
110 		goto out;
111 	}
112 	*xp = X509_dup(sk_X509_value(certs, 0));
113 
114 	/* Verify that we have eContent to disseminate. */
115 
116 	if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) {
117 		warnx("%s: RFC 6488 section 2.1.4: "
118 		    "eContent: zero-length content", fn);
119 		goto out;
120 	}
121 
122 	/*
123 	 * Extract and duplicate the eContent.
124 	 * The CMS framework offers us no other way of easily managing
125 	 * this information; and since we're going to d2i it anyway,
126 	 * simply pass it as the desired underlying types.
127 	 */
128 
129 	if ((res = malloc((*os)->length)) == NULL)
130 		err(1, NULL);
131 	memcpy(res, (*os)->data, (*os)->length);
132 	*rsz = (*os)->length;
133 
134 	rc = 1;
135 out:
136 	BIO_free_all(bio);
137 	sk_X509_free(certs);
138 	CMS_ContentInfo_free(cms);
139 
140 	if (rc == 0) {
141 		X509_free(*xp);
142 		*xp = NULL;
143 	}
144 
145 	return res;
146 }
147