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