xref: /openbsd-src/usr.sbin/rpki-client/tak.c (revision 30a085025d55af7cc5799cf5d05a8d03f20d20ee)
1 /*	$OpenBSD: tak.c,v 1.21 2024/11/13 12:51:04 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 extern ASN1_OBJECT	*tak_oid;
35 
36 /*
37  * ASN.1 templates for Trust Anchor Keys (draft-ietf-sidrops-signed-tal-12)
38  */
39 
40 ASN1_ITEM_EXP TAKey_it;
41 ASN1_ITEM_EXP TAK_it;
42 
43 DECLARE_STACK_OF(ASN1_IA5STRING);
44 
45 #ifndef DEFINE_STACK_OF
46 #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
47 #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
48 #endif
49 
50 typedef struct {
51 	STACK_OF(ASN1_UTF8STRING)	*comments;
52 	STACK_OF(ASN1_IA5STRING)	*certificateURIs;
53 	X509_PUBKEY			*subjectPublicKeyInfo;
54 } TAKey;
55 
56 typedef struct {
57 	ASN1_INTEGER			*version;
58 	TAKey				*current;
59 	TAKey				*predecessor;
60 	TAKey				*successor;
61 } TAK;
62 
63 ASN1_SEQUENCE(TAKey) = {
64 	ASN1_SEQUENCE_OF(TAKey, comments, ASN1_UTF8STRING),
65 	ASN1_SEQUENCE_OF(TAKey, certificateURIs, ASN1_IA5STRING),
66 	ASN1_SIMPLE(TAKey, subjectPublicKeyInfo, X509_PUBKEY),
67 } ASN1_SEQUENCE_END(TAKey);
68 
69 ASN1_SEQUENCE(TAK) = {
70 	ASN1_EXP_OPT(TAK, version, ASN1_INTEGER, 0),
71 	ASN1_SIMPLE(TAK, current, TAKey),
72 	ASN1_EXP_OPT(TAK, predecessor, TAKey, 0),
73 	ASN1_EXP_OPT(TAK, successor, TAKey, 1),
74 } ASN1_SEQUENCE_END(TAK);
75 
76 DECLARE_ASN1_FUNCTIONS(TAK);
77 IMPLEMENT_ASN1_FUNCTIONS(TAK);
78 
79 /*
80  * On success return pointer to allocated & valid takey structure,
81  * on failure return NULL.
82  */
83 static struct takey *
84 parse_takey(const char *fn, const TAKey *takey)
85 {
86 	const ASN1_UTF8STRING	*comment;
87 	const ASN1_IA5STRING	*certURI;
88 	X509_PUBKEY		*pubkey;
89 	struct takey		*res = NULL;
90 	unsigned char		*der = NULL;
91 	size_t			 i;
92 	int			 der_len;
93 
94 	if ((res = calloc(1, sizeof(struct takey))) == NULL)
95 		err(1, NULL);
96 
97 	res->num_comments = sk_ASN1_UTF8STRING_num(takey->comments);
98 	if (res->num_comments > 0) {
99 		res->comments = calloc(res->num_comments, sizeof(char *));
100 		if (res->comments == NULL)
101 			err(1, NULL);
102 
103 		for (i = 0; i < res->num_comments; i++) {
104 			comment = sk_ASN1_UTF8STRING_value(takey->comments, i);
105 			res->comments[i] = strndup(comment->data,
106 			    comment->length);
107 			if (res->comments[i] == NULL)
108 				err(1, NULL);
109 		}
110 	}
111 
112 	res->num_uris = sk_ASN1_IA5STRING_num(takey->certificateURIs);
113 	if (res->num_uris == 0) {
114 		warnx("%s: Signed TAL requires at least 1 CertificateURI", fn);
115 		goto err;
116 	}
117 	if ((res->uris = calloc(res->num_uris, sizeof(char *))) == NULL)
118 		err(1, NULL);
119 
120 	for (i = 0; i < res->num_uris; i++) {
121 		certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i);
122 		if (!valid_uri(certURI->data, certURI->length, NULL)) {
123 			warnx("%s: invalid TA URI", fn);
124 			goto err;
125 		}
126 
127 		/* XXX: enforce that protocol is rsync or https. */
128 
129 		res->uris[i] = strndup(certURI->data, certURI->length);
130 		if (res->uris[i] == NULL)
131 			err(1, NULL);
132 	}
133 
134 	pubkey = takey->subjectPublicKeyInfo;
135 	if ((res->ski = x509_pubkey_get_ski(pubkey, fn)) == NULL)
136 		goto err;
137 
138 	if ((der_len = i2d_X509_PUBKEY(pubkey, &der)) <= 0) {
139 		warnx("%s: i2d_X509_PUBKEY failed", fn);
140 		goto err;
141 	}
142 	res->pubkey = der;
143 	res->pubkeysz = der_len;
144 
145 	return res;
146 
147  err:
148 	takey_free(res);
149 	return NULL;
150 }
151 
152 /*
153  * Parses the eContent segment of an TAK file
154  * Returns zero on failure, non-zero on success.
155  */
156 static int
157 tak_parse_econtent(const char *fn, struct tak *tak, const unsigned char *d,
158     size_t dsz)
159 {
160 	const unsigned char	*oder;
161 	TAK			*tak_asn1;
162 	int			 rc = 0;
163 
164 	oder = d;
165 	if ((tak_asn1 = d2i_TAK(NULL, &d, dsz)) == NULL) {
166 		warnx("%s: failed to parse Trust Anchor Key", fn);
167 		goto out;
168 	}
169 	if (d != oder + dsz) {
170 		warnx("%s: %td bytes trailing garbage in eContent", fn,
171 		    oder + dsz - d);
172 		goto out;
173 	}
174 
175 	if (!valid_econtent_version(fn, tak_asn1->version, 0))
176 		goto out;
177 
178 	tak->current = parse_takey(fn, tak_asn1->current);
179 	if (tak->current == NULL)
180 		goto out;
181 
182 	if (tak_asn1->predecessor != NULL) {
183 		tak->predecessor = parse_takey(fn, tak_asn1->predecessor);
184 		if (tak->predecessor == NULL)
185 			goto out;
186 	}
187 
188 	if (tak_asn1->successor != NULL) {
189 		tak->successor = parse_takey(fn, tak_asn1->successor);
190 		if (tak->successor == NULL)
191 			goto out;
192 	}
193 
194 	rc = 1;
195  out:
196 	TAK_free(tak_asn1);
197 	return rc;
198 }
199 
200 /*
201  * Parse a full draft-ietf-sidrops-signed-tal file.
202  * Returns the TAK or NULL if the object was malformed.
203  */
204 struct tak *
205 tak_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
206     size_t len)
207 {
208 	struct tak		*tak;
209 	struct cert		*cert = NULL;
210 	unsigned char		*cms;
211 	size_t			 cmsz;
212 	time_t			 signtime = 0;
213 	int			 rc = 0;
214 
215 	cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz, &signtime);
216 	if (cms == NULL)
217 		return NULL;
218 
219 	if ((tak = calloc(1, sizeof(struct tak))) == NULL)
220 		err(1, NULL);
221 	tak->signtime = signtime;
222 
223 	if (!x509_get_aia(*x509, fn, &tak->aia))
224 		goto out;
225 	if (!x509_get_aki(*x509, fn, &tak->aki))
226 		goto out;
227 	if (!x509_get_sia(*x509, fn, &tak->sia))
228 		goto out;
229 	if (!x509_get_ski(*x509, fn, &tak->ski))
230 		goto out;
231 	if (tak->aia == NULL || tak->aki == NULL || tak->sia == NULL ||
232 	    tak->ski == NULL) {
233 		warnx("%s: RFC 6487 section 4.8: "
234 		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
235 		goto out;
236 	}
237 
238 	if (!x509_get_notbefore(*x509, fn, &tak->notbefore))
239 		goto out;
240 	if (!x509_get_notafter(*x509, fn, &tak->notafter))
241 		goto out;
242 
243 	if (!x509_inherits(*x509)) {
244 		warnx("%s: RFC 3779 extension not set to inherit", fn);
245 		goto out;
246 	}
247 
248 	if (!tak_parse_econtent(fn, tak, cms, cmsz))
249 		goto out;
250 
251 	if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
252 		goto out;
253 
254 	if (strcmp(tak->aki, tak->current->ski) != 0) {
255 		warnx("%s: current TAKey's SKI does not match EE AKI", fn);
256 		goto out;
257 	}
258 
259 	rc = 1;
260  out:
261 	if (rc == 0) {
262 		tak_free(tak);
263 		tak = NULL;
264 		X509_free(*x509);
265 		*x509 = NULL;
266 	}
267 	cert_free(cert);
268 	free(cms);
269 	return tak;
270 }
271 
272 /*
273  * Free TAKey pointer.
274  */
275 void
276 takey_free(struct takey *t)
277 {
278 	size_t	i;
279 
280 	if (t == NULL)
281 		return;
282 
283 	for (i = 0; i < t->num_comments; i++)
284 		free(t->comments[i]);
285 
286 	for (i = 0; i < t->num_uris; i++)
287 		free(t->uris[i]);
288 
289 	free(t->comments);
290 	free(t->uris);
291 	free(t->ski);
292 	free(t->pubkey);
293 	free(t);
294 }
295 
296 /*
297  * Free an TAK pointer.
298  * Safe to call with NULL.
299  */
300 void
301 tak_free(struct tak *t)
302 {
303 	if (t == NULL)
304 		return;
305 
306 	takey_free(t->current);
307 	takey_free(t->predecessor);
308 	takey_free(t->successor);
309 
310 	free(t->aia);
311 	free(t->aki);
312 	free(t->sia);
313 	free(t->ski);
314 	free(t);
315 }
316