xref: /openbsd-src/usr.sbin/rpki-client/tak.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: tak.c,v 1.8 2023/03/12 11:46:35 tb Exp $ */
2 /*
3  * Copyright (c) 2022 Job Snijders <job@fastly.com>
4  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
5  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <err.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include <openssl/asn1.h>
26 #include <openssl/asn1t.h>
27 #include <openssl/safestack.h>
28 #include <openssl/stack.h>
29 #include <openssl/x509.h>
30 #include <openssl/x509v3.h>
31 
32 #include "extern.h"
33 
34 /*
35  * Parse results and data of the Trust Anchor Key file.
36  */
37 struct parse {
38 	const char	*fn; /* TAK file name */
39 	struct tak	*res; /* results */
40 };
41 
42 extern ASN1_OBJECT	*tak_oid;
43 
44 /*
45  * ASN.1 templates for Trust Anchor Keys (draft-ietf-sidrops-signed-tal-12)
46  */
47 
48 DECLARE_STACK_OF(ASN1_IA5STRING);
49 
50 #ifndef DEFINE_STACK_OF
51 #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
52 #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
53 #endif
54 
55 typedef struct {
56 	STACK_OF(ASN1_UTF8STRING)	*comments;
57 	STACK_OF(ASN1_IA5STRING)	*certificateURIs;
58 	X509_PUBKEY			*subjectPublicKeyInfo;
59 } TAKey;
60 
61 typedef struct {
62 	ASN1_INTEGER			*version;
63 	TAKey				*current;
64 	TAKey				*predecessor;
65 	TAKey				*successor;
66 } TAK;
67 
68 ASN1_SEQUENCE(TAKey) = {
69 	ASN1_SEQUENCE_OF(TAKey, comments, ASN1_UTF8STRING),
70 	ASN1_SEQUENCE_OF(TAKey, certificateURIs, ASN1_IA5STRING),
71 	ASN1_SIMPLE(TAKey, subjectPublicKeyInfo, X509_PUBKEY),
72 } ASN1_SEQUENCE_END(TAKey);
73 
74 ASN1_SEQUENCE(TAK) = {
75 	ASN1_EXP_OPT(TAK, version, ASN1_INTEGER, 0),
76 	ASN1_SIMPLE(TAK, current, TAKey),
77 	ASN1_EXP_OPT(TAK, predecessor, TAKey, 0),
78 	ASN1_EXP_OPT(TAK, successor, TAKey, 1),
79 } ASN1_SEQUENCE_END(TAK);
80 
81 DECLARE_ASN1_FUNCTIONS(TAK);
82 IMPLEMENT_ASN1_FUNCTIONS(TAK);
83 
84 /*
85  * On success return pointer to allocated & valid takey structure,
86  * on failure return NULL.
87  */
88 static struct takey *
89 parse_takey(const char *fn, const TAKey *takey)
90 {
91 	const ASN1_UTF8STRING	*comment;
92 	const ASN1_IA5STRING	*certURI;
93 	EVP_PKEY		*pkey;
94 	RSA			*r;
95 	struct takey		*res = NULL;
96 	unsigned char		*der = NULL, *rder = NULL;
97 	unsigned char		 md[SHA_DIGEST_LENGTH];
98 	size_t			 i;
99 	int			 rdersz, rc = 0;
100 
101 	if ((res = calloc(1, sizeof(struct takey))) == NULL)
102 		err(1, NULL);
103 
104 	res->commentsz = sk_ASN1_UTF8STRING_num(takey->comments);
105 	if (res->commentsz > 0) {
106 		res->comments = calloc(res->commentsz, sizeof(char *));
107 		if (res->comments == NULL)
108 			err(1, NULL);
109 
110 		for (i = 0; i < res->commentsz; i++) {
111 			comment = sk_ASN1_UTF8STRING_value(takey->comments, i);
112 			res->comments[i] = strndup(comment->data, comment->length);
113 			if (res->comments[i] == NULL)
114 				err(1, NULL);
115 		}
116 	}
117 
118 	res->urisz = sk_ASN1_IA5STRING_num(takey->certificateURIs);
119 	if (res->urisz == 0) {
120 		warnx("%s: Signed TAL requires at least 1 CertificateURI", fn);
121 		goto out;
122 	}
123 	if ((res->uris = calloc(res->urisz, sizeof(char *))) == NULL)
124 		err(1, NULL);
125 
126 	for (i = 0; i < res->urisz; i++) {
127 		certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i);
128 		if (!valid_uri(certURI->data, certURI->length, NULL)) {
129 			warnx("%s: invalid TA URI", fn);
130 			goto out;
131 		}
132 
133 		/* XXX: enforce that protocol is rsync or https. */
134 
135 		res->uris[i] = strndup(certURI->data, certURI->length);
136 		if (res->uris[i] == NULL)
137 			err(1, NULL);
138 	}
139 
140 	if ((pkey = X509_PUBKEY_get0(takey->subjectPublicKeyInfo)) == NULL) {
141 		warnx("%s: X509_PUBKEY_get0 failed", fn);
142 		goto out;
143 	}
144 
145 	if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) {
146 		warnx("%s: EVP_PKEY_get0_RSA failed", fn);
147 		goto out;
148 	}
149 
150 	if ((rdersz = i2d_RSAPublicKey(r, &rder)) <= 0) {
151 		warnx("%s: i2d_RSAPublicKey failed", fn);
152 		goto out;
153 	}
154 
155 	if (!EVP_Digest(rder, rdersz, md, NULL, EVP_sha1(), NULL)) {
156 		warnx("%s: EVP_Digest failed", fn);
157 		goto out;
158 	}
159 	res->ski = hex_encode(md, SHA_DIGEST_LENGTH);
160 
161 	if ((res->pubkeysz = i2d_PUBKEY(pkey, &der)) <= 0) {
162 		warnx("%s: i2d_PUBKEY failed", fn);
163 		goto out;
164 	}
165 
166 	res->pubkey = der;
167 	der = NULL;
168 
169 	rc = 1;
170  out:
171 	if (rc == 0) {
172 		takey_free(res);
173 		res = NULL;
174 	}
175 	free(der);
176 	free(rder);
177 	return res;
178 }
179 
180 /*
181  * Parses the eContent segment of an TAK file
182  * Returns zero on failure, non-zero on success.
183  */
184 static int
185 tak_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
186 {
187 	TAK		*tak;
188 	const char	*fn;
189 	int		 rc = 0;
190 
191 	fn = p->fn;
192 
193 	if ((tak = d2i_TAK(NULL, &d, dsz)) == NULL) {
194 		cryptowarnx("%s: failed to parse Trust Anchor Key", fn);
195 		goto out;
196 	}
197 
198 	if (!valid_econtent_version(fn, tak->version))
199 		goto out;
200 
201 	p->res->current = parse_takey(fn, tak->current);
202 	if (p->res->current == NULL)
203 		goto out;
204 
205 	if (tak->predecessor != NULL) {
206 		p->res->predecessor = parse_takey(fn, tak->predecessor);
207 		if (p->res->predecessor == NULL)
208 			goto out;
209 	}
210 
211 	if (tak->successor != NULL) {
212 		p->res->successor = parse_takey(fn, tak->successor);
213 		if (p->res->successor == NULL)
214 			goto out;
215 	}
216 
217 	rc = 1;
218  out:
219 	TAK_free(tak);
220 	return rc;
221 }
222 
223 /*
224  * Parse a full draft-ietf-sidrops-signed-tal file.
225  * Returns the TAK or NULL if the object was malformed.
226  */
227 struct tak *
228 tak_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
229 {
230 	struct parse		 p;
231 	unsigned char		*cms;
232 	size_t			 cmsz;
233 	time_t			 signtime = 0;
234 	int			 rc = 0;
235 
236 	memset(&p, 0, sizeof(struct parse));
237 	p.fn = fn;
238 
239 	cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz, &signtime);
240 	if (cms == NULL)
241 		return NULL;
242 
243 	if ((p.res = calloc(1, sizeof(struct tak))) == NULL)
244 		err(1, NULL);
245 	p.res->signtime = signtime;
246 
247 	if (!x509_get_aia(*x509, fn, &p.res->aia))
248 		goto out;
249 	if (!x509_get_aki(*x509, fn, &p.res->aki))
250 		goto out;
251 	if (!x509_get_sia(*x509, fn, &p.res->sia))
252 		goto out;
253 	if (!x509_get_ski(*x509, fn, &p.res->ski))
254 		goto out;
255 	if (p.res->aia == NULL || p.res->aki == NULL || p.res->sia == NULL ||
256 	    p.res->ski == NULL) {
257 		warnx("%s: RFC 6487 section 4.8: "
258 		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
259 		goto out;
260 	}
261 
262 	if (!x509_get_notbefore(*x509, fn, &p.res->notbefore))
263 		goto out;
264 	if (!x509_get_notafter(*x509, fn, &p.res->notafter))
265 		goto out;
266 
267 	if (!x509_inherits(*x509)) {
268 		warnx("%s: RFC 3779 extension not set to inherit", fn);
269 		goto out;
270 	}
271 
272 	if (!tak_parse_econtent(cms, cmsz, &p))
273 		goto out;
274 
275 	if (strcmp(p.res->aki, p.res->current->ski) != 0) {
276 		warnx("%s: current TAKey's SKI does not match EE AKI", fn);
277 		goto out;
278 	}
279 
280 	rc = 1;
281  out:
282 	if (rc == 0) {
283 		tak_free(p.res);
284 		p.res = NULL;
285 		X509_free(*x509);
286 		*x509 = NULL;
287 	}
288 	free(cms);
289 	return p.res;
290 }
291 
292 /*
293  * Free TAKey pointer.
294  */
295 void
296 takey_free(struct takey *t)
297 {
298 	size_t	i;
299 
300 	if (t == NULL)
301 		return;
302 
303 	for (i = 0; i < t->commentsz; i++)
304 		free(t->comments[i]);
305 
306 	for (i = 0; i < t->urisz; i++)
307 		free(t->uris[i]);
308 
309 	free(t->comments);
310 	free(t->uris);
311 	free(t->ski);
312 	free(t->pubkey);
313 	free(t);
314 }
315 
316 /*
317  * Free an TAK pointer.
318  * Safe to call with NULL.
319  */
320 void
321 tak_free(struct tak *t)
322 {
323 	if (t == NULL)
324 		return;
325 
326 	takey_free(t->current);
327 	takey_free(t->predecessor);
328 	takey_free(t->successor);
329 
330 	free(t->aia);
331 	free(t->aki);
332 	free(t->ski);
333 	free(t);
334 }
335